camstack 1.2.60 → 1.2.62

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-BPlfW-CG.js
23637
- var require_dist_BPlfW_CG = __commonJS({
23638
- "../system/dist/dist-BPlfW-CG.js"(exports) {
23636
+ // ../system/dist/dist-CDgIzo82.js
23637
+ var require_dist_CDgIzo82 = __commonJS({
23638
+ "../system/dist/dist-CDgIzo82.js"(exports) {
23639
23639
  "use strict";
23640
23640
  var zod = require_zod();
23641
23641
  var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
@@ -26256,29 +26256,50 @@ var require_dist_BPlfW_CG = __commonJS({
26256
26256
  });
26257
26257
  var StorageMigrationLeaseInputSchema = zod.z.object({ leaseId: zod.z.string().min(1) });
26258
26258
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: zod.z.string().min(1) });
26259
+ var MediaRelocateModeSchema = zod.z.enum([
26260
+ "move",
26261
+ "seal",
26262
+ "gallery"
26263
+ ]);
26259
26264
  var RelocateMediaInputSchema = zod.z.object({
26260
26265
  toLocationId: zod.z.string(),
26261
- throttleMbps: zod.z.number().min(1).max(1e3).optional()
26266
+ throttleMbps: zod.z.number().min(1).max(1e3).optional(),
26267
+ /** Omitted = `move`, the pre-existing behaviour. */
26268
+ mode: MediaRelocateModeSchema.optional()
26269
+ });
26270
+ var UnstampedEventMediaCountSchema = zod.z.object({
26271
+ media: zod.z.number().int().nonnegative(),
26272
+ retrainFrames: zod.z.number().int().nonnegative(),
26273
+ total: zod.z.number().int().nonnegative()
26262
26274
  });
26263
26275
  var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: zod.z.string().min(1) });
26264
26276
  var StorageMigrationClassSchema = zod.z.enum([
26265
26277
  "recordings",
26266
26278
  "recordingsLow",
26267
- "eventMedia"
26279
+ "eventMedia",
26280
+ "backups",
26281
+ "galleryMedia"
26268
26282
  ]);
26269
26283
  var StorageMigrationDestinationsSchema = zod.z.object({
26270
26284
  recordings: zod.z.string().min(1).optional(),
26271
26285
  recordingsLow: zod.z.string().min(1).optional(),
26272
- eventMedia: zod.z.string().min(1).optional()
26286
+ eventMedia: zod.z.string().min(1).optional(),
26287
+ backups: zod.z.string().min(1).optional(),
26288
+ galleryMedia: zod.z.string().min(1).optional()
26273
26289
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
26290
+ var StorageMigrationModeSchema = zod.z.enum(["blocking", "nonBlocking"]);
26274
26291
  var StorageMigrationInputSchema = zod.z.object({
26275
26292
  destinations: StorageMigrationDestinationsSchema,
26276
- throttleMbps: zod.z.number().min(1).max(1e3).optional()
26293
+ throttleMbps: zod.z.number().min(1).max(1e3).optional(),
26294
+ /** Omitted = `blocking`, which stays the default. */
26295
+ mode: StorageMigrationModeSchema.optional()
26277
26296
  });
26278
26297
  var StorageMigrationPhaseSchema = zod.z.enum([
26279
26298
  "planning",
26299
+ "sealing",
26280
26300
  "pausing",
26281
26301
  "moving",
26302
+ "draining",
26282
26303
  "verifying",
26283
26304
  "repointing",
26284
26305
  "refreshing",
@@ -26303,6 +26324,9 @@ var require_dist_BPlfW_CG = __commonJS({
26303
26324
  var StorageMigrationJobSchema = zod.z.object({
26304
26325
  jobId: zod.z.string(),
26305
26326
  phase: StorageMigrationPhaseSchema,
26327
+ /** Which order this job is running. `status` is the only place an operator
26328
+ * can tell a seconds-long cutover from a thirty-hour one. */
26329
+ mode: StorageMigrationModeSchema,
26306
26330
  destinations: StorageMigrationDestinationsSchema,
26307
26331
  throttleMbps: zod.z.number(),
26308
26332
  moves: zod.z.array(StorageMigrationMoveSchema),
@@ -26315,13 +26339,31 @@ var require_dist_BPlfW_CG = __commonJS({
26315
26339
  finishedAt: zod.z.number().nullable(),
26316
26340
  error: zod.z.string().nullable()
26317
26341
  });
26342
+ var StorageMigrationFindingCodeSchema = zod.z.enum([
26343
+ "sharesDeviceWithSource",
26344
+ "deviceIdentityUnknown",
26345
+ "unstampedEventMediaRows",
26346
+ "blockingOnly",
26347
+ "noMover"
26348
+ ]);
26349
+ var StorageMigrationFindingSchema = zod.z.object({
26350
+ code: StorageMigrationFindingCodeSchema,
26351
+ storageClass: StorageMigrationClassSchema,
26352
+ /** Human-readable, already carrying the ids and counts. */
26353
+ message: zod.z.string()
26354
+ });
26318
26355
  var StorageMigrationPlanSchema = zod.z.object({
26319
26356
  destinations: StorageMigrationDestinationsSchema,
26357
+ /** The mode this plan was built for. A plan is only valid for its mode: the
26358
+ * `eventMedia` seal gate and the single-cardinality refusal both depend on
26359
+ * it. */
26360
+ mode: StorageMigrationModeSchema,
26320
26361
  moves: zod.z.array(zod.z.object({
26321
26362
  storageClass: StorageMigrationClassSchema,
26322
26363
  fromLocationId: zod.z.string(),
26323
26364
  toLocationId: zod.z.string()
26324
- }))
26365
+ })),
26366
+ findings: zod.z.array(StorageMigrationFindingSchema)
26325
26367
  });
26326
26368
  var StorageLocationTypeSchema = zod.z.string().regex(/^[a-z][a-zA-Z0-9-]*$/);
26327
26369
  var StorageLocationSchema = zod.z.object({
@@ -37316,6 +37358,17 @@ var require_dist_BPlfW_CG = __commonJS({
37316
37358
  kind: "mutation",
37317
37359
  auth: "admin"
37318
37360
  }),
37361
+ /**
37362
+ * How many media / retrain rows still carry NO `locationId`.
37363
+ *
37364
+ * A NULL row means "wherever `eventMedia` points NOW", so the instant a
37365
+ * repoint moves that pointer every such row reads from the new disk while
37366
+ * its bytes are on the old one — the archive goes dark until a drain
37367
+ * happens to stamp it. This count is what the migration planner's
37368
+ * non-blocking gate reads; `relocateMedia({ mode: 'seal' })` is what drives
37369
+ * it to zero.
37370
+ */
37371
+ countUnstampedEventMedia: method(zod.z.object({}), UnstampedEventMediaCountSchema, { auth: "admin" }),
37319
37372
  /** Every relocate job this addon knows about, newest first (in RAM: the
37320
37373
  * move is resumable, so a lost list costs nothing but the display). */
37321
37374
  listRelocateMediaJobs: method(zod.z.object({}), zod.z.array(RelocateJobSchema).readonly(), {
@@ -39768,7 +39821,24 @@ var require_dist_BPlfW_CG = __commonJS({
39768
39821
  kind: "mutation",
39769
39822
  auth: "admin"
39770
39823
  }),
39771
- deleteLocation: method(zod.z.object({ id: zod.z.string() }), zod.z.void(), {
39824
+ /**
39825
+ * Remove a location record. REFUSES a location that still holds data —
39826
+ * deleting a drained location whose durable rows still name it is how this
39827
+ * hub acquired 2 131 ghost `recordings:high` segments, and playback does
39828
+ * not stat, so the operator sees a silent black window rather than an
39829
+ * error.
39830
+ *
39831
+ * `force` exists because the occupancy check is BEST-EFFORT and refuses on
39832
+ * "unknown" as well as on "occupied" (a read that fails must not authorise
39833
+ * a destruction — D49). A location on a removed disk, on another node, or
39834
+ * behind a remote provider answers "unknown" forever, and a refusal an
39835
+ * operator cannot override is its own failure mode. `force: true` is
39836
+ * logged, loudly, with what the check saw.
39837
+ */
39838
+ deleteLocation: method(zod.z.object({
39839
+ id: zod.z.string(),
39840
+ force: zod.z.boolean().optional()
39841
+ }), zod.z.void(), {
39772
39842
  kind: "mutation",
39773
39843
  auth: "admin"
39774
39844
  }),
@@ -52862,6 +52932,12 @@ var require_dist_BPlfW_CG = __commonJS({
52862
52932
  addonId: null,
52863
52933
  access: "create"
52864
52934
  },
52935
+ "pipelineAnalytics.countUnstampedEventMedia": {
52936
+ capName: "pipeline-analytics",
52937
+ capScope: "device",
52938
+ addonId: null,
52939
+ access: "view"
52940
+ },
52865
52941
  "pipelineAnalytics.deleteDeviceEvents": {
52866
52942
  capName: "pipeline-analytics",
52867
52943
  capScope: "device",
@@ -58713,7 +58789,7 @@ var require_alerts_addon = __commonJS({
58713
58789
  [Symbol.toStringTag]: { value: "Module" }
58714
58790
  });
58715
58791
  require_chunk_Cek0wNdY();
58716
- var require_dist10 = require_dist_BPlfW_CG();
58792
+ var require_dist10 = require_dist_CDgIzo82();
58717
58793
  function selectExpired(alerts, cutoffMs) {
58718
58794
  return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
58719
58795
  }
@@ -59532,7 +59608,7 @@ var require_console_logging = __commonJS({
59532
59608
  [Symbol.toStringTag]: { value: "Module" }
59533
59609
  });
59534
59610
  require_chunk_Cek0wNdY();
59535
- var require_dist10 = require_dist_BPlfW_CG();
59611
+ var require_dist10 = require_dist_CDgIzo82();
59536
59612
  var require_formatter = require_formatter_DqAKDlvN();
59537
59613
  var LEVEL_RANK = {
59538
59614
  debug: 0,
@@ -59626,7 +59702,7 @@ var require_core_blocks_addon = __commonJS({
59626
59702
  "use strict";
59627
59703
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
59628
59704
  var require_chunk = require_chunk_Cek0wNdY();
59629
- var require_dist10 = require_dist_BPlfW_CG();
59705
+ var require_dist10 = require_dist_CDgIzo82();
59630
59706
  var node_crypto = __require("crypto");
59631
59707
  var node_fs_promises = __require("fs/promises");
59632
59708
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -60523,11 +60599,11 @@ var require_core_blocks = __commonJS({
60523
60599
  }
60524
60600
  });
60525
60601
 
60526
- // ../system/dist/retired-settings-keys-DBY6ebwV.js
60527
- var require_retired_settings_keys_DBY6ebwV = __commonJS({
60528
- "../system/dist/retired-settings-keys-DBY6ebwV.js"(exports) {
60602
+ // ../system/dist/retired-settings-keys-BfAzWvPC.js
60603
+ var require_retired_settings_keys_BfAzWvPC = __commonJS({
60604
+ "../system/dist/retired-settings-keys-BfAzWvPC.js"(exports) {
60529
60605
  "use strict";
60530
- var require_dist10 = require_dist_BPlfW_CG();
60606
+ var require_dist10 = require_dist_CDgIzo82();
60531
60607
  function settingsStoreIsAuthoritativeHere(env) {
60532
60608
  const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
60533
60609
  return raw === "" || raw === "hub";
@@ -62741,8 +62817,8 @@ var require_device_manager_addon = __commonJS({
62741
62817
  [Symbol.toStringTag]: { value: "Module" }
62742
62818
  });
62743
62819
  require_chunk_Cek0wNdY();
62744
- var require_dist10 = require_dist_BPlfW_CG();
62745
- var require_retired_settings_keys = require_retired_settings_keys_DBY6ebwV();
62820
+ var require_dist10 = require_dist_CDgIzo82();
62821
+ var require_retired_settings_keys = require_retired_settings_keys_BfAzWvPC();
62746
62822
  var node_crypto = __require("crypto");
62747
62823
  var _camstack_types_node = require_node();
62748
62824
  var JOB_HISTORY = 20;
@@ -67491,7 +67567,7 @@ var require_hub_forwarder = __commonJS({
67491
67567
  [Symbol.toStringTag]: { value: "Module" }
67492
67568
  });
67493
67569
  require_chunk_Cek0wNdY();
67494
- var require_dist10 = require_dist_BPlfW_CG();
67570
+ var require_dist10 = require_dist_CDgIzo82();
67495
67571
  var require_formatter = require_formatter_DqAKDlvN();
67496
67572
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
67497
67573
  var HubForwarderDestination = class {
@@ -67628,7 +67704,7 @@ var require_liveness_monitor_addon = __commonJS({
67628
67704
  "use strict";
67629
67705
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
67630
67706
  require_chunk_Cek0wNdY();
67631
- var require_dist10 = require_dist_BPlfW_CG();
67707
+ var require_dist10 = require_dist_CDgIzo82();
67632
67708
  var NO_DEVICES = "liveness:no-devices";
67633
67709
  var ALL_OFFLINE = "liveness:all-devices-offline";
67634
67710
  var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
@@ -67818,7 +67894,7 @@ var require_local_auth_addon = __commonJS({
67818
67894
  [Symbol.toStringTag]: { value: "Module" }
67819
67895
  });
67820
67896
  var require_chunk = require_chunk_Cek0wNdY();
67821
- var require_dist10 = require_dist_BPlfW_CG();
67897
+ var require_dist10 = require_dist_CDgIzo82();
67822
67898
  var node_crypto = __require("crypto");
67823
67899
  node_crypto = require_chunk.__toESM(node_crypto);
67824
67900
  var crypto$1 = __require("crypto");
@@ -75631,7 +75707,7 @@ var require_loki_logging = __commonJS({
75631
75707
  [Symbol.toStringTag]: { value: "Module" }
75632
75708
  });
75633
75709
  require_chunk_Cek0wNdY();
75634
- var require_dist10 = require_dist_BPlfW_CG();
75710
+ var require_dist10 = require_dist_CDgIzo82();
75635
75711
  function sanitizeLabelName(raw) {
75636
75712
  const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
75637
75713
  return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
@@ -76196,7 +76272,7 @@ var require_native_metrics_addon = __commonJS({
76196
76272
  [Symbol.toStringTag]: { value: "Module" }
76197
76273
  });
76198
76274
  var require_chunk = require_chunk_Cek0wNdY();
76199
- var require_dist10 = require_dist_BPlfW_CG();
76275
+ var require_dist10 = require_dist_CDgIzo82();
76200
76276
  var node_fs_promises = __require("fs/promises");
76201
76277
  var node_child_process = __require("child_process");
76202
76278
  var node_util = __require("util");
@@ -78818,7 +78894,7 @@ var require_filesystem_storage_addon = __commonJS({
78818
78894
  [Symbol.toStringTag]: { value: "Module" }
78819
78895
  });
78820
78896
  var require_chunk = require_chunk_Cek0wNdY();
78821
- var require_dist10 = require_dist_BPlfW_CG();
78897
+ var require_dist10 = require_dist_CDgIzo82();
78822
78898
  var node_crypto = __require("crypto");
78823
78899
  var node_fs_promises = __require("fs/promises");
78824
78900
  var node_path = __require("path");
@@ -79934,8 +80010,8 @@ var require_sqlite_settings_addon = __commonJS({
79934
80010
  [Symbol.toStringTag]: { value: "Module" }
79935
80011
  });
79936
80012
  var require_chunk = require_chunk_Cek0wNdY();
79937
- var require_dist10 = require_dist_BPlfW_CG();
79938
- var require_retired_settings_keys = require_retired_settings_keys_DBY6ebwV();
80013
+ var require_dist10 = require_dist_CDgIzo82();
80014
+ var require_retired_settings_keys = require_retired_settings_keys_BfAzWvPC();
79939
80015
  var node_crypto = __require("crypto");
79940
80016
  var node_fs = __require("fs");
79941
80017
  var node_module = __require("module");
@@ -82214,7 +82290,7 @@ var require_storage_orchestrator_addon = __commonJS({
82214
82290
  [Symbol.toStringTag]: { value: "Module" }
82215
82291
  });
82216
82292
  var require_chunk = require_chunk_Cek0wNdY();
82217
- var require_dist10 = require_dist_BPlfW_CG();
82293
+ var require_dist10 = require_dist_CDgIzo82();
82218
82294
  var node_crypto = __require("crypto");
82219
82295
  var node_fs_promises = __require("fs/promises");
82220
82296
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -82397,8 +82473,19 @@ var require_storage_orchestrator_addon = __commonJS({
82397
82473
  var STORAGE_CLASSES = [
82398
82474
  "recordings",
82399
82475
  "recordingsLow",
82400
- "eventMedia"
82476
+ "eventMedia",
82477
+ "backups",
82478
+ "galleryMedia"
82479
+ ];
82480
+ var MOVER_CLASSES = [
82481
+ "recordings",
82482
+ "recordingsLow",
82483
+ "eventMedia",
82484
+ "galleryMedia"
82401
82485
  ];
82486
+ var BLOCKING_ONLY_CLASSES = ["galleryMedia"];
82487
+ var GALLERY_FORBIDDEN_NEIGHBOUR_TYPES = ["recordings", "recordingsLow"];
82488
+ var MOVE_LANES = ["media", "footage"];
82402
82489
  var PARTICIPANTS = [
82403
82490
  "pipeline",
82404
82491
  "recorder",
@@ -82418,7 +82505,9 @@ var require_storage_orchestrator_addon = __commonJS({
82418
82505
  this.deps = deps;
82419
82506
  }
82420
82507
  async plan(input) {
82508
+ const mode = input.mode ?? "blocking";
82421
82509
  const moves = [];
82510
+ const findings = [];
82422
82511
  for (const storageClass of STORAGE_CLASSES) {
82423
82512
  const targetId = input.destinations[storageClass];
82424
82513
  if (targetId === void 0) continue;
@@ -82428,6 +82517,9 @@ var require_storage_orchestrator_addon = __commonJS({
82428
82517
  if (!target) throw new Error(`Storage location "${targetId}" not found`);
82429
82518
  if (target.type !== storageClass) throw new Error(`Storage location "${targetId}" is type "${target.type}", expected "${storageClass}"`);
82430
82519
  if (source.id === target.id) throw new Error(`Storage location "${targetId}" is already the "${storageClass}" default`);
82520
+ if (!MOVER_CLASSES.includes(storageClass)) throw new Error(`No mover owns "${storageClass}" \u2014 the migration can repoint its default but cannot move its bytes. Move it by hand, then repoint the default with upsertLocation.`);
82521
+ if (mode === "nonBlocking" && BLOCKING_ONLY_CLASSES.includes(storageClass)) throw new Error(`"${storageClass}" is a single-cardinality storage class: it can never exist at two locations at once, so it cannot be repointed first and drained behind. Use mode "blocking".`);
82522
+ await this.refuseGalleryOntoRecordingsVolume(storageClass, targetId);
82431
82523
  moves.push({
82432
82524
  storageClass,
82433
82525
  fromLocationId: source.id,
@@ -82436,17 +82528,79 @@ var require_storage_orchestrator_addon = __commonJS({
82436
82528
  state: null,
82437
82529
  error: null
82438
82530
  });
82531
+ if (BLOCKING_ONLY_CLASSES.includes(storageClass)) findings.push({
82532
+ code: "blockingOnly",
82533
+ storageClass,
82534
+ message: `"${storageClass}" is single-cardinality: this move is stop-the-world for its whole duration, in every mode. It is a few KB per enrolled sample, which is why that is acceptable.`
82535
+ });
82536
+ findings.push(...await this.deviceFindings(storageClass, source.id, target.id));
82439
82537
  }
82440
82538
  if (moves.length === 0) throw new Error("select at least one storage class");
82539
+ if (moves.some((move) => move.storageClass === "eventMedia")) {
82540
+ const unstamped = await this.deps.participants.analytics.countUnstamped();
82541
+ if (unstamped.total > 0) {
82542
+ if (mode === "nonBlocking") throw new Error(`${unstamped.total.toString()} event-media row(s) still carry no locationId (${unstamped.media.toString()} media, ${unstamped.retrainFrames.toString()} retrain frames). A non-blocking cutover would silently orphan them. Run the seal first: pipelineAnalytics.relocateMedia({ mode: "seal", toLocationId: "${this.deps.locations.getDefaultLocation("eventMedia")?.id ?? "eventMedia"}" }).`);
82543
+ findings.push({
82544
+ code: "unstampedEventMediaRows",
82545
+ storageClass: "eventMedia",
82546
+ message: `${unstamped.total.toString()} event-media row(s) carry no locationId. This blocking migration stamps them as it moves them, but a non-blocking one would be refused until a seal pass drives the count to zero.`
82547
+ });
82548
+ }
82549
+ }
82441
82550
  return {
82442
82551
  destinations: input.destinations,
82552
+ mode,
82443
82553
  moves: moves.map(({ storageClass, fromLocationId, toLocationId }) => ({
82444
82554
  storageClass,
82445
82555
  fromLocationId,
82446
82556
  toLocationId
82447
- }))
82557
+ })),
82558
+ findings
82448
82559
  };
82449
82560
  }
82561
+ /**
82562
+ * HARD refusal: `galleryMedia` onto a volume a recordings class lives on.
82563
+ *
82564
+ * This is the one same-device case that is a refusal rather than a finding,
82565
+ * because its consequence is silent and arrives months later — the gallery is
82566
+ * wiped by the next footage wipe, quota eviction or recordings disk swap, and
82567
+ * nothing at that moment points back at this migration.
82568
+ *
82569
+ * It can only refuse what it can SEE. `deviceKeyOf` is a realpath, so it
82570
+ * catches a symlink, a bind mount and a literally-shared directory — and
82571
+ * misses two distinct directories on one filesystem, and any location pinned
82572
+ * to another node. Those come back `null`/distinct and produce a
82573
+ * `deviceIdentityUnknown` finding instead.
82574
+ */
82575
+ async refuseGalleryOntoRecordingsVolume(storageClass, targetId) {
82576
+ if (storageClass !== "galleryMedia") return;
82577
+ const targetKey = await this.deviceKey(targetId);
82578
+ if (targetKey === null) return;
82579
+ for (const type of GALLERY_FORBIDDEN_NEIGHBOUR_TYPES) for (const neighbour of this.deps.locations.listLocations({ type })) {
82580
+ if (await this.deviceKey(neighbour.id) !== targetKey) continue;
82581
+ throw new Error(`Storage location "${targetId}" shares a volume with "${neighbour.id}" (${type}). galleryMedia is deliberately NOT on the recordings volume: enrolment images survive every retention sweep, so they must also survive a footage wipe, a quota eviction and a recordings disk swap. Choose a destination on another volume.`);
82582
+ }
82583
+ }
82584
+ /** Same-device observations that are reported, not refused. */
82585
+ async deviceFindings(storageClass, sourceId, targetId) {
82586
+ if (this.deps.deviceKeyOf === void 0) return [];
82587
+ const [sourceKey, targetKey] = await Promise.all([this.deviceKey(sourceId), this.deviceKey(targetId)]);
82588
+ if (sourceKey === null || targetKey === null) return [{
82589
+ code: "deviceIdentityUnknown",
82590
+ storageClass,
82591
+ message: `Could not establish whether "${sourceId}" and "${targetId}" are on the same device (a location with no basePath, or pinned to another node \u2014 the realpath would have to be taken there). Note also that this check is PATH identity, not st_dev: two distinct directories on one filesystem are never detected as one device.`
82592
+ }];
82593
+ if (sourceKey !== targetKey) return [];
82594
+ return [{
82595
+ code: "sharesDeviceWithSource",
82596
+ storageClass,
82597
+ message: `"${targetId}" resolves to the same path as "${sourceId}". The move will re-stamp rows rather than copy bytes, and it buys no redundancy \u2014 both "locations" are one disk.`
82598
+ }];
82599
+ }
82600
+ async deviceKey(locationId) {
82601
+ if (this.deps.deviceKeyOf === void 0) return null;
82602
+ return this.deps.deviceKeyOf(locationId);
82603
+ }
82450
82604
  async start(input) {
82451
82605
  if (this.startReserved) throw new Error("storage migration is already active");
82452
82606
  this.startReserved = true;
@@ -82463,6 +82617,7 @@ var require_storage_orchestrator_addon = __commonJS({
82463
82617
  const job = {
82464
82618
  jobId: this.deps.newId(),
82465
82619
  phase: "planning",
82620
+ mode: plan.mode,
82466
82621
  destinations: input.destinations,
82467
82622
  throttleMbps: input.throttleMbps ?? 40,
82468
82623
  moves: plan.moves.map((move) => ({
@@ -82496,7 +82651,8 @@ var require_storage_orchestrator_addon = __commonJS({
82496
82651
  }
82497
82652
  async cancel(jobId) {
82498
82653
  const job = await this.status(jobId);
82499
- if (!job || isTerminal(job) || job.repointed) return false;
82654
+ const cutoverInFlight = job !== null && job.repointed && (job.phase === "refreshing" || job.phase === "resuming");
82655
+ if (!job || isTerminal(job) || cutoverInFlight) return false;
82500
82656
  job.cancelRequested = true;
82501
82657
  await this.persist(job);
82502
82658
  await Promise.all(job.moves.filter((move) => move.moverJobId !== null).map((move) => this.cancelMove(move)));
@@ -82527,7 +82683,7 @@ var require_storage_orchestrator_addon = __commonJS({
82527
82683
  job.pauseLeaseId ??= this.deps.newId();
82528
82684
  if (job.repointed && job.phase === "resuming") job.phase = "refreshing";
82529
82685
  await this.persist(job);
82530
- try {
82686
+ if (!runsUnleased(job)) try {
82531
82687
  await this.pauseParticipants(job, true);
82532
82688
  } catch (err) {
82533
82689
  job.error = err instanceof Error ? err.message : String(err);
@@ -82539,14 +82695,34 @@ var require_storage_orchestrator_addon = __commonJS({
82539
82695
  this.runPromise;
82540
82696
  }
82541
82697
  }
82698
+ /**
82699
+ * The state machine. Both modes run the SAME phase handlers; only the order
82700
+ * differs, which is the whole of the non-blocking design — no second mover,
82701
+ * no parallel code path, nothing that can drift between the two.
82702
+ *
82703
+ * blocking: planning → pausing → moving → verifying → repointing →
82704
+ * refreshing → resuming → done
82705
+ * nonBlocking: planning → sealing → pausing → repointing → refreshing →
82706
+ * resuming → draining → verifying → done
82707
+ *
82708
+ * Written as a straight-line sequence of `if (phase === X)` blocks so boot
82709
+ * recovery re-enters at whatever phase the durable record says, exactly as
82710
+ * before.
82711
+ */
82542
82712
  async run(job) {
82713
+ const nonBlocking = job.mode === "nonBlocking";
82543
82714
  try {
82544
- if (job.phase === "planning") await this.setPhase(job, "pausing");
82715
+ if (job.phase === "planning") await this.setPhase(job, nonBlocking ? "sealing" : "pausing");
82716
+ if (job.phase === "sealing") {
82717
+ await this.sealEventMedia(job);
82718
+ if (job.cancelRequested) return this.finishCancelled(job);
82719
+ await this.setPhase(job, "pausing");
82720
+ }
82545
82721
  if (job.phase === "pausing") {
82546
82722
  job.pauseLeaseId ??= job.jobId;
82547
82723
  await this.pauseParticipants(job);
82548
82724
  if (job.cancelRequested) return this.finishCancelled(job);
82549
- await this.setPhase(job, "moving");
82725
+ await this.setPhase(job, nonBlocking ? "repointing" : "moving");
82550
82726
  }
82551
82727
  if (job.phase === "moving") {
82552
82728
  await this.startOrResumeMoves(job);
@@ -82554,7 +82730,7 @@ var require_storage_orchestrator_addon = __commonJS({
82554
82730
  if (job.cancelRequested) return this.finishCancelled(job);
82555
82731
  await this.setPhase(job, "verifying");
82556
82732
  }
82557
- if (job.phase === "verifying") {
82733
+ if (job.phase === "verifying" && !nonBlocking) {
82558
82734
  await this.verifyMoves(job);
82559
82735
  if (job.cancelRequested) return this.finishCancelled(job);
82560
82736
  await this.setPhase(job, "repointing");
@@ -82567,17 +82743,28 @@ var require_storage_orchestrator_addon = __commonJS({
82567
82743
  }
82568
82744
  if (job.phase === "refreshing") {
82569
82745
  const leaseId = requireLease(job);
82570
- if (job.moves.some((move) => move.storageClass !== "eventMedia")) await this.deps.participants.recorder.refresh(leaseId);
82571
- if (job.moves.some((move) => move.storageClass === "eventMedia")) await this.deps.participants.analytics.refresh(leaseId);
82746
+ if (job.moves.some((move) => laneOf(move.storageClass) === "footage")) await this.deps.participants.recorder.refresh(leaseId);
82747
+ if (job.moves.some((move) => laneOf(move.storageClass) === "media")) await this.deps.participants.analytics.refresh(leaseId);
82572
82748
  await this.setPhase(job, "resuming");
82573
82749
  }
82574
82750
  if (job.phase === "resuming") {
82575
82751
  await this.resumeParticipants(job);
82752
+ await this.setPhase(job, nonBlocking ? "draining" : "done");
82753
+ }
82754
+ if (job.phase === "draining") {
82755
+ await this.startOrResumeMoves(job);
82756
+ await this.waitForMoves(job);
82757
+ if (job.cancelRequested) return this.finishCancelled(job);
82758
+ await this.setPhase(job, "verifying");
82759
+ }
82760
+ if (job.phase === "verifying" && nonBlocking) {
82761
+ await this.verifyMoves(job);
82762
+ if (job.cancelRequested) return this.finishCancelled(job);
82576
82763
  await this.setPhase(job, "done");
82577
82764
  }
82578
82765
  } catch (err) {
82579
82766
  job.error = err instanceof Error ? err.message : String(err);
82580
- if (job.repointed) {
82767
+ if (job.repointed && job.pausedParticipants.length > 0) {
82581
82768
  job.phase = "refreshing";
82582
82769
  job.finishedAt = null;
82583
82770
  await this.persist(job);
@@ -82626,21 +82813,28 @@ var require_storage_orchestrator_addon = __commonJS({
82626
82813
  }
82627
82814
  }
82628
82815
  }
82816
+ /**
82817
+ * Start whatever is startable, honouring the ONE constraint the engines
82818
+ * impose: each is single-flight.
82819
+ *
82820
+ * Two lanes — the recorder's footage engine and post-analysis's media engine
82821
+ * — run in parallel with each other and strictly FIFO within themselves.
82822
+ * `galleryMedia` shares the media lane with `eventMedia` because it shares
82823
+ * the same `MediaRelocateEngine`, which refuses a second running job; starting
82824
+ * both would fail the second one instantly.
82825
+ */
82629
82826
  async startOrResumeMoves(job) {
82630
82827
  if (job.cancelRequested) return;
82631
- const mediaMove = job.moves.find((move) => move.storageClass === "eventMedia");
82632
- if (mediaMove?.moverJobId === null) {
82633
- mediaMove.moverJobId = await this.startMove(job, mediaMove);
82634
- await this.persist(job);
82635
- }
82636
- const footageMoves = job.moves.filter((move) => move.storageClass !== "eventMedia");
82637
- for (let index = 0; index < footageMoves.length; index++) {
82638
- const move = footageMoves[index];
82639
- if (move.moverJobId !== null) continue;
82640
- if (!footageMoves.slice(0, index).every((predecessor) => predecessor.state === "done")) return;
82641
- move.moverJobId = await this.startMove(job, move);
82642
- await this.persist(job);
82643
- return;
82828
+ for (const lane of MOVE_LANES) {
82829
+ const laneMoves = job.moves.filter((move) => laneOf(move.storageClass) === lane);
82830
+ for (let index = 0; index < laneMoves.length; index++) {
82831
+ const move = laneMoves[index];
82832
+ if (move === void 0 || move.moverJobId !== null) continue;
82833
+ if (!laneMoves.slice(0, index).every((predecessor) => predecessor.state === "done")) break;
82834
+ move.moverJobId = await this.startMove(job, move);
82835
+ await this.persist(job);
82836
+ break;
82837
+ }
82644
82838
  }
82645
82839
  }
82646
82840
  async waitForMoves(job) {
@@ -82651,7 +82845,7 @@ var require_storage_orchestrator_addon = __commonJS({
82651
82845
  let complete = true;
82652
82846
  for (const [index, move] of job.moves.entries()) {
82653
82847
  if (job.cancelRequested && move.moverJobId === null) continue;
82654
- if (move.storageClass !== "eventMedia" && job.moves.slice(0, index).filter((predecessor) => predecessor.storageClass !== "eventMedia").some((predecessor) => predecessor.state !== "done")) {
82848
+ if (job.moves.slice(0, index).filter((predecessor) => laneOf(predecessor.storageClass) === laneOf(move.storageClass)).some((predecessor) => predecessor.state !== "done")) {
82655
82849
  complete = false;
82656
82850
  continue;
82657
82851
  }
@@ -82685,30 +82879,92 @@ var require_storage_orchestrator_addon = __commonJS({
82685
82879
  await sleep(pollMs);
82686
82880
  }
82687
82881
  }
82882
+ /**
82883
+ * Nail every unstamped `eventMedia`/retrain row to the location its bytes are
82884
+ * ALREADY on, before anything repoints. Runs with every writer up — it moves
82885
+ * no bytes at all.
82886
+ *
82887
+ * The gate is the RE-COUNT, not the seal job's terminal state: a seal that
82888
+ * failed some rows still finishes, and "finished" is not "there are none
82889
+ * left". A non-zero count here fails the job while nothing has been paused
82890
+ * and nothing has been repointed, which is the cheapest possible place to
82891
+ * discover it.
82892
+ *
82893
+ * The seal's own mover job id is deliberately NOT durable. It is idempotent
82894
+ * and cheap, so a coordinator restart mid-seal simply re-runs the whole
82895
+ * phase — and the count that follows is what actually decides, so a lost job
82896
+ * id cannot make the gate pass.
82897
+ */
82898
+ async sealEventMedia(job) {
82899
+ const move = job.moves.find((candidate) => candidate.storageClass === "eventMedia");
82900
+ if (move === void 0) return;
82901
+ if ((await this.deps.participants.analytics.countUnstamped()).total > 0) {
82902
+ const started = await this.deps.participants.analytics.startDrain({
82903
+ toLocationId: move.fromLocationId,
82904
+ mode: "seal"
82905
+ });
82906
+ await this.waitForSeal(job, started.jobId);
82907
+ if (job.cancelRequested) return;
82908
+ }
82909
+ const after = await this.deps.participants.analytics.countUnstamped();
82910
+ if (after.total > 0) throw new Error(`event-media seal left ${after.total.toString()} row(s) without a locationId (${after.media.toString()} media, ${after.retrainFrames.toString()} retrain frames). A non-blocking cutover would silently orphan them; nothing has been paused or repointed.`);
82911
+ }
82912
+ async waitForSeal(job, moverJobId) {
82913
+ const sleep = this.deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
82914
+ const pollMs = this.deps.pollMs ?? 250;
82915
+ for (; ; ) {
82916
+ if (job.cancelRequested) {
82917
+ await this.deps.participants.analytics.cancelMove(moverJobId);
82918
+ return;
82919
+ }
82920
+ const status = await this.deps.participants.analytics.getMove(moverJobId);
82921
+ if (status === null || status.state === "done" || status.state === "cancelled") return;
82922
+ if (status.state === "failed") throw new Error(status.error ?? "event-media seal failed");
82923
+ await sleep(pollMs);
82924
+ }
82925
+ }
82688
82926
  async verifyMoves(job) {
82689
82927
  for (const move of job.moves) if (move.state !== "done") throw new Error(`${move.storageClass} move did not complete verification`);
82690
82928
  }
82929
+ /**
82930
+ * Arm one class's mover.
82931
+ *
82932
+ * `draining` runs after every writer has been resumed, so there is no lease
82933
+ * left to present — it goes through the participants' UNLEASED entry points
82934
+ * (`recording.relocateFootage` / `pipelineAnalytics.relocateMedia`), which
82935
+ * are the same engines, not a second mover.
82936
+ */
82691
82937
  async startMove(job, move) {
82692
- if (move.storageClass === "eventMedia") return (await this.deps.participants.analytics.startMove({
82693
- toLocationId: move.toLocationId,
82694
- throttleMbps: job.throttleMbps,
82695
- leaseId: requireLease(job)
82696
- })).jobId;
82697
- return (await this.deps.participants.recorder.startMove({
82938
+ const unleased = runsUnleased(job);
82939
+ if (laneOf(move.storageClass) === "media") {
82940
+ const input2 = {
82941
+ toLocationId: move.toLocationId,
82942
+ throttleMbps: job.throttleMbps,
82943
+ mode: move.storageClass === "galleryMedia" ? "gallery" : "move"
82944
+ };
82945
+ return (unleased ? await this.deps.participants.analytics.startDrain(input2) : await this.deps.participants.analytics.startMove({
82946
+ ...input2,
82947
+ leaseId: requireLease(job)
82948
+ })).jobId;
82949
+ }
82950
+ const input = {
82698
82951
  fromLocationId: move.fromLocationId,
82699
82952
  toLocationId: move.toLocationId,
82700
- footageClass: move.storageClass,
82701
- throttleMbps: job.throttleMbps,
82953
+ footageClass: move.storageClass === "recordingsLow" ? "recordingsLow" : "recordings",
82954
+ throttleMbps: job.throttleMbps
82955
+ };
82956
+ return (unleased ? await this.deps.participants.recorder.startDrain(input) : await this.deps.participants.recorder.startMove({
82957
+ ...input,
82702
82958
  leaseId: requireLease(job)
82703
82959
  })).jobId;
82704
82960
  }
82705
82961
  async moveStatus(move) {
82706
82962
  if (move.moverJobId === null) return null;
82707
- return move.storageClass === "eventMedia" ? this.deps.participants.analytics.getMove(move.moverJobId) : this.deps.participants.recorder.getMove(move.moverJobId);
82963
+ return laneOf(move.storageClass) === "media" ? this.deps.participants.analytics.getMove(move.moverJobId) : this.deps.participants.recorder.getMove(move.moverJobId);
82708
82964
  }
82709
82965
  async cancelMove(move) {
82710
82966
  if (move.moverJobId === null) return;
82711
- if (move.storageClass === "eventMedia") await this.deps.participants.analytics.cancelMove(move.moverJobId);
82967
+ if (laneOf(move.storageClass) === "media") await this.deps.participants.analytics.cancelMove(move.moverJobId);
82712
82968
  else await this.deps.participants.recorder.cancelMove(move.moverJobId);
82713
82969
  }
82714
82970
  participant(name) {
@@ -82737,6 +82993,13 @@ var require_storage_orchestrator_addon = __commonJS({
82737
82993
  function isTerminal(job) {
82738
82994
  return job.phase === "done" || job.phase === "failed" || job.phase === "cancelled";
82739
82995
  }
82996
+ function laneOf(storageClass) {
82997
+ return storageClass === "eventMedia" || storageClass === "galleryMedia" ? "media" : "footage";
82998
+ }
82999
+ function runsUnleased(job) {
83000
+ if (job.phase === "sealing" || job.phase === "draining") return true;
83001
+ return job.phase === "verifying" && job.mode === "nonBlocking";
83002
+ }
82740
83003
  function buildStorageLocationRegistry(perAddon) {
82741
83004
  const map = /* @__PURE__ */ new Map();
82742
83005
  for (const addonDeclarations of perAddon) for (const declaration of addonDeclarations) {
@@ -82842,6 +83105,14 @@ var require_storage_orchestrator_addon = __commonJS({
82842
83105
  * early-boot path that predates the resolver wiring.
82843
83106
  */
82844
83107
  nodeLocalResolver = null;
83108
+ /**
83109
+ * Injected occupancy probe (see {@link LocationOccupancyProbe}). `null` until
83110
+ * {@link setOccupancyProbe} runs, and a `null` probe answers `unknown` — so a
83111
+ * service with no probe REFUSES an unforced delete rather than performing one
83112
+ * it could not check. The check is the point; a check that defaults to
83113
+ * "permit" is a paragraph, not a guard.
83114
+ */
83115
+ occupancyProbe = null;
82845
83116
  /** `localNodeId` with any forked-child `/addon` suffix stripped. */
82846
83117
  localNode;
82847
83118
  /**
@@ -82941,6 +83212,15 @@ var require_storage_orchestrator_addon = __commonJS({
82941
83212
  this.nodeLocalResolver = resolver;
82942
83213
  }
82943
83214
  /**
83215
+ * Inject the "does this location still hold anything" probe read by
83216
+ * {@link deleteLocation}. Absent → every delete sees `unknown` and therefore
83217
+ * REFUSES without `force`, which is the safe direction for a service wired
83218
+ * without one.
83219
+ */
83220
+ setOccupancyProbe(probe) {
83221
+ this.occupancyProbe = probe;
83222
+ }
83223
+ /**
82944
83224
  * The full set of addon-declared storage locations, as aggregated by
82945
83225
  * the kernel and injected via {@link setRegistry}. Powers the admin-UI
82946
83226
  * Data screen's per-declaration grouping. Empty until the registry is
@@ -83164,13 +83444,22 @@ var require_storage_orchestrator_addon = __commonJS({
83164
83444
  * Persistence (Task 6) mirrors the delete asynchronously, with errors
83165
83445
  * routed to the logger — see `upsertLocation` for the rationale.
83166
83446
  */
83167
- deleteLocation(id) {
83447
+ async deleteLocation(id, options) {
83168
83448
  const loc = this.locations.get(id);
83169
83449
  if (!loc) throw new Error(`Storage location "${id}" not found`);
83170
83450
  if (loc.isSystem) throw new Error(`Storage location "${id}" is system-managed and cannot be deleted. Edit its config (path / providerId) instead.`);
83171
83451
  if (loc.isDefault) {
83172
83452
  if (![...this.locations.values()].find((l) => l.type === loc.type && l.id !== id && l.isDefault)) throw new Error(`Cannot delete default location "${id}" for type "${loc.type}" \u2014 promote another location to default first`);
83173
83453
  }
83454
+ const occupancy = await this.probeOccupancy(loc);
83455
+ if (occupancy !== "empty") {
83456
+ if (options?.force !== true) throw new Error(occupancy === "occupied" ? `Storage location "${id}" still holds data \u2014 drain it first (storage migration / relocate), or pass force to delete the record anyway and strand what is on it` : `Storage location "${id}" could not be checked for remaining data (unreachable provider, another node, or an unmounted root) \u2014 pass force to delete the record anyway`);
83457
+ this.logger.warn("storage-orchestrator: FORCED delete of a location that was not verified empty", { meta: {
83458
+ id,
83459
+ type: loc.type,
83460
+ occupancy
83461
+ } });
83462
+ }
83174
83463
  this.locations.delete(id);
83175
83464
  if (this.locationStore) this.locationStore.delete(id).catch((err) => {
83176
83465
  this.logger.error("storage-orchestrator: delete persistence failed", { meta: {
@@ -83180,6 +83469,23 @@ var require_storage_orchestrator_addon = __commonJS({
83180
83469
  });
83181
83470
  }
83182
83471
  /**
83472
+ * Run the injected occupancy probe, converting "no probe" and "the probe
83473
+ * threw" into `unknown` rather than into a permit.
83474
+ */
83475
+ async probeOccupancy(location) {
83476
+ const probe = this.occupancyProbe;
83477
+ if (probe === null) return "unknown";
83478
+ try {
83479
+ return await probe(location);
83480
+ } catch (err) {
83481
+ this.logger.warn("storage-orchestrator: occupancy probe failed", { meta: {
83482
+ id: location.id,
83483
+ error: err instanceof Error ? err.message : String(err)
83484
+ } });
83485
+ return "unknown";
83486
+ }
83487
+ }
83488
+ /**
83183
83489
  * Remove system-seeded locations whose type is no longer declared by any
83184
83490
  * addon (stale defaults from a removed location type). Operator-added
83185
83491
  * (non-system) locations of an undeclared type are KEPT but warned — the
@@ -83575,6 +83881,7 @@ var require_storage_orchestrator_addon = __commonJS({
83575
83881
  });
83576
83882
  this.service = service;
83577
83883
  service.setNodeLocalResolver((providerId) => this.nodeLocalByProvider.get(providerId));
83884
+ service.setOccupancyProbe((location) => this.locationOccupancy(location));
83578
83885
  await service.initialize();
83579
83886
  const provider = {
83580
83887
  listLocations: async ({ type }) => {
@@ -83587,8 +83894,8 @@ var require_storage_orchestrator_addon = __commonJS({
83587
83894
  getDefaultLocation: async ({ type }) => service.getDefaultLocation(type),
83588
83895
  listLocationDeclarations: async () => service.listDeclarations(),
83589
83896
  upsertLocation: async (input) => service.upsertLocation(input),
83590
- deleteLocation: async ({ id }) => {
83591
- service.deleteLocation(id);
83897
+ deleteLocation: async ({ id, force }) => {
83898
+ await service.deleteLocation(id, { force: force === true });
83592
83899
  },
83593
83900
  testLocation: async ({ id }) => {
83594
83901
  const loc = service.getLocationById(id);
@@ -83751,6 +84058,7 @@ var require_storage_orchestrator_addon = __commonJS({
83751
84058
  await this.ctx.api.recording.resumeForStorageMigration.mutate({ leaseId });
83752
84059
  },
83753
84060
  startMove: (input) => this.ctx.api.recording.startStorageMigrationMove.mutate(input),
84061
+ startDrain: (input) => this.ctx.api.recording.relocateFootage.mutate(input),
83754
84062
  getMove: (jobId) => this.ctx.api.recording.getStorageMigrationMoveStatus.query({ jobId }),
83755
84063
  cancelMove: async (jobId) => (await this.ctx.api.recording.cancelStorageMigrationMove.mutate({ jobId })).cancelled,
83756
84064
  refresh: async (leaseId) => {
@@ -83765,6 +84073,8 @@ var require_storage_orchestrator_addon = __commonJS({
83765
84073
  await this.ctx.api.pipelineAnalytics.resumeForStorageMigration.mutate({ leaseId });
83766
84074
  },
83767
84075
  startMove: (input) => this.ctx.api.pipelineAnalytics.startStorageMigrationMove.mutate(input),
84076
+ startDrain: (input) => this.ctx.api.pipelineAnalytics.relocateMedia.mutate(input),
84077
+ countUnstamped: () => this.ctx.api.pipelineAnalytics.countUnstampedEventMedia.query({}),
83768
84078
  getMove: (jobId) => this.ctx.api.pipelineAnalytics.getStorageMigrationMoveStatus.query({ jobId }),
83769
84079
  cancelMove: async (jobId) => (await this.ctx.api.pipelineAnalytics.cancelStorageMigrationMove.mutate({ jobId })).cancelled,
83770
84080
  refresh: async (leaseId) => {
@@ -83773,7 +84083,8 @@ var require_storage_orchestrator_addon = __commonJS({
83773
84083
  }
83774
84084
  },
83775
84085
  now: () => Date.now(),
83776
- newId: () => (0, node_crypto.randomUUID)()
84086
+ newId: () => (0, node_crypto.randomUUID)(),
84087
+ deviceKeyOf: (locationId) => this.locationDeviceKey(locationId)
83777
84088
  });
83778
84089
  this.migration = migration;
83779
84090
  const migrationProvider = {
@@ -83988,6 +84299,44 @@ var require_storage_orchestrator_addon = __commonJS({
83988
84299
  return node_path.resolve(bp);
83989
84300
  }
83990
84301
  }
84302
+ /**
84303
+ * Does this location still hold anything? A SHALLOW `readdir` of its
84304
+ * `basePath` — one syscall, never a walk. `/recordings` holds one entry per
84305
+ * camera, so this is cheap on a volume with millions of segments, and a
84306
+ * `find`-style walk of a saturated recordings disk is precisely what must
84307
+ * not happen here.
84308
+ *
84309
+ * **What it cannot see, stated rather than papered over:**
84310
+ *
84311
+ * - A location on ANOTHER node, or behind a remote provider (S3 / SFTP /
84312
+ * WebDAV): its bytes are not on this filesystem and this probe answers
84313
+ * `unknown`, never `empty`.
84314
+ * - A location with no `basePath` — same answer.
84315
+ * - The difference between "holds live footage" and "holds one stray
84316
+ * lock-file". It measures BYTES ON DISK, not durable rows, so it refuses
84317
+ * a location holding orphan files that no index names. That direction is
84318
+ * the safe one, and `force` is the escape hatch for it.
84319
+ *
84320
+ * A missing root (`ENOENT`) is `empty`, not `unknown`: an unmounted disk and
84321
+ * a deleted directory are indistinguishable here, and if the record's own
84322
+ * path does not exist there is nothing this delete can strand. Every OTHER
84323
+ * errno — `EACCES`, `EIO`, a stale NFS handle — is `unknown` and refuses.
84324
+ */
84325
+ async locationOccupancy(location) {
84326
+ if ((location.nodeId === void 0 || location.nodeId === "" ? HUB_NODE_ID : location.nodeId) !== (this.service?.getLocalNodeId() ?? HUB_NODE_ID)) return "unknown";
84327
+ const basePath = this.locationBasePath(location.id);
84328
+ if (basePath === null) return "unknown";
84329
+ try {
84330
+ return (await node_fs_promises.readdir(basePath)).length > 0 ? "occupied" : "empty";
84331
+ } catch (err) {
84332
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") return "empty";
84333
+ this.ctx.logger.warn("storage-orchestrator: could not read a location root to check it", { meta: {
84334
+ id: location.id,
84335
+ error: err instanceof Error ? err.message : String(err)
84336
+ } });
84337
+ return "unknown";
84338
+ }
84339
+ }
83991
84340
  /** Free capacity (%) on a location's volume via `statfs`; 100 (guard inert) when unstattable. */
83992
84341
  async locationFreePercent(locationId) {
83993
84342
  const bp = this.locationBasePath(locationId);
@@ -84095,7 +84444,7 @@ var require_system_config_addon = __commonJS({
84095
84444
  [Symbol.toStringTag]: { value: "Module" }
84096
84445
  });
84097
84446
  require_chunk_Cek0wNdY();
84098
- var require_dist10 = require_dist_BPlfW_CG();
84447
+ var require_dist10 = require_dist_CDgIzo82();
84099
84448
  var SECTION_TITLES = {
84100
84449
  server: "Server",
84101
84450
  auth: "Authentication"
@@ -102156,7 +102505,7 @@ var require_winston_logging = __commonJS({
102156
102505
  [Symbol.toStringTag]: { value: "Module" }
102157
102506
  });
102158
102507
  var require_chunk = require_chunk_Cek0wNdY();
102159
- var require_dist10 = require_dist_BPlfW_CG();
102508
+ var require_dist10 = require_dist_CDgIzo82();
102160
102509
  var require_formatter = require_formatter_DqAKDlvN();
102161
102510
  var node_path = __require("path");
102162
102511
  node_path = require_chunk.__toESM(node_path);
@@ -104099,9 +104448,9 @@ var require_event_category_BaEgqJNv = __commonJS({
104099
104448
  }
104100
104449
  });
104101
104450
 
104102
- // ../types/dist/sleep-DUxF5DdC.js
104103
- var require_sleep_DUxF5DdC = __commonJS({
104104
- "../types/dist/sleep-DUxF5DdC.js"(exports) {
104451
+ // ../types/dist/sleep-CJrvRDlD.js
104452
+ var require_sleep_CJrvRDlD = __commonJS({
104453
+ "../types/dist/sleep-CJrvRDlD.js"(exports) {
104105
104454
  "use strict";
104106
104455
  var require_event_category = require_event_category_BaEgqJNv();
104107
104456
  var zod = require_zod();
@@ -106801,6 +107150,7 @@ var require_sleep_DUxF5DdC = __commonJS({
106801
107150
  getStorageMigrationMoveStatus: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getStorageMigrationMoveStatus", "query", input),
106802
107151
  cancelStorageMigrationMove: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "cancelStorageMigrationMove", "mutation", input),
106803
107152
  relocateMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "relocateMedia", "mutation", input),
107153
+ countUnstampedEventMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "countUnstampedEventMedia", "query", input),
106804
107154
  listRelocateMediaJobs: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listRelocateMediaJobs", "query", input),
106805
107155
  cancelRelocateMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "cancelRelocateMedia", "mutation", input),
106806
107156
  listOpsLog: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listOpsLog", "query", input),
@@ -107765,7 +108115,7 @@ var require_addon = __commonJS({
107765
108115
  "use strict";
107766
108116
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
107767
108117
  var require_event_category = require_event_category_BaEgqJNv();
107768
- var require_sleep = require_sleep_DUxF5DdC();
108118
+ var require_sleep = require_sleep_CJrvRDlD();
107769
108119
  var require_err_msg = require_err_msg_COpsHMw2();
107770
108120
  var CAP_INPUT_DEFAULTS = Object.freeze({
107771
108121
  "addons": { "getLogs": { "limit": 100 } },
@@ -114644,12 +114994,12 @@ var require_dist2 = __commonJS({
114644
114994
  }
114645
114995
  });
114646
114996
 
114647
- // ../system/dist/manifest-python-deps-COeSr7el.js
114648
- var require_manifest_python_deps_COeSr7el = __commonJS({
114649
- "../system/dist/manifest-python-deps-COeSr7el.js"(exports) {
114997
+ // ../system/dist/manifest-python-deps-DVODn-qc.js
114998
+ var require_manifest_python_deps_DVODn_qc = __commonJS({
114999
+ "../system/dist/manifest-python-deps-DVODn-qc.js"(exports) {
114650
115000
  "use strict";
114651
115001
  var require_chunk = require_chunk_Cek0wNdY();
114652
- require_dist_BPlfW_CG();
115002
+ require_dist_CDgIzo82();
114653
115003
  var node_crypto = __require("crypto");
114654
115004
  node_crypto = require_chunk.__toESM(node_crypto);
114655
115005
  var _camstack_types_node = require_node();
@@ -114755,6 +115105,152 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
114755
115105
  const top = window2.top.map((d) => `${d.childId}:${d.sent}/${d.suppressed}`).join(",");
114756
115106
  return ` fanoutMode=uds:${window2.udsMode}/cross-node:${window2.crossNodeMode} children=${window2.childrenConnected} childrenUndeclared=${window2.childrenUndeclared} fanoutSent=${window2.fanoutSent} fanoutSuppressed=${window2.fanoutSuppressed} crossNodeDelivered=${window2.crossNodeDelivered} crossNodeSuppressed=${window2.crossNodeSuppressed}` + (top.length === 0 ? "" : ` fanoutTop=${top}`);
114757
115107
  }
115108
+ function createSocketDirectionCounters() {
115109
+ return {
115110
+ reqMessages: 0,
115111
+ reqBytes: 0,
115112
+ resMessages: 0,
115113
+ resBytes: 0,
115114
+ evtMessages: 0,
115115
+ evtBytes: 0
115116
+ };
115117
+ }
115118
+ function recordSocketFrame(counters, kind, bytes) {
115119
+ if (kind === "evt") {
115120
+ counters.evtMessages += 1;
115121
+ counters.evtBytes += bytes;
115122
+ return;
115123
+ }
115124
+ if (kind === "req") {
115125
+ counters.reqMessages += 1;
115126
+ counters.reqBytes += bytes;
115127
+ return;
115128
+ }
115129
+ counters.resMessages += 1;
115130
+ counters.resBytes += bytes;
115131
+ }
115132
+ function sampleSocketDirection(counters) {
115133
+ return {
115134
+ reqMessages: counters.reqMessages,
115135
+ reqBytes: counters.reqBytes,
115136
+ resMessages: counters.resMessages,
115137
+ resBytes: counters.resBytes,
115138
+ evtMessages: counters.evtMessages,
115139
+ evtBytes: counters.evtBytes
115140
+ };
115141
+ }
115142
+ var EMPTY_SOCKET_DIRECTION = {
115143
+ reqMessages: 0,
115144
+ reqBytes: 0,
115145
+ resMessages: 0,
115146
+ resBytes: 0,
115147
+ evtMessages: 0,
115148
+ evtBytes: 0
115149
+ };
115150
+ function socketDirectionMessages(sample) {
115151
+ return sample.reqMessages + sample.resMessages + sample.evtMessages;
115152
+ }
115153
+ function socketDirectionBytes(sample) {
115154
+ return sample.reqBytes + sample.resBytes + sample.evtBytes;
115155
+ }
115156
+ function createSocketPlaneReader(sources) {
115157
+ return () => {
115158
+ try {
115159
+ const registry = sources.registry();
115160
+ if (registry === null) return void 0;
115161
+ const listed = registry.listChildren();
115162
+ const peers = [];
115163
+ for (const child of listed) {
115164
+ const traffic = registry.getChildSocketTraffic(child.childId);
115165
+ if (traffic === null) continue;
115166
+ peers.push({
115167
+ peerId: child.childId,
115168
+ tx: traffic.tx,
115169
+ rx: traffic.rx
115170
+ });
115171
+ }
115172
+ return {
115173
+ peers,
115174
+ peersConnected: listed.length
115175
+ };
115176
+ } catch {
115177
+ return;
115178
+ }
115179
+ };
115180
+ }
115181
+ var SOCKET_PLANE_TOP_N = 5;
115182
+ var EMPTY_SOCKET_PLANE_BASELINE = { peers: /* @__PURE__ */ new Map() };
115183
+ function diffDirection(current, before) {
115184
+ const previous = before ?? EMPTY_SOCKET_DIRECTION;
115185
+ return {
115186
+ reqMessages: Math.max(0, current.reqMessages - previous.reqMessages),
115187
+ reqBytes: Math.max(0, current.reqBytes - previous.reqBytes),
115188
+ resMessages: Math.max(0, current.resMessages - previous.resMessages),
115189
+ resBytes: Math.max(0, current.resBytes - previous.resBytes),
115190
+ evtMessages: Math.max(0, current.evtMessages - previous.evtMessages),
115191
+ evtBytes: Math.max(0, current.evtBytes - previous.evtBytes)
115192
+ };
115193
+ }
115194
+ function addDirection(into, add) {
115195
+ return {
115196
+ reqMessages: into.reqMessages + add.reqMessages,
115197
+ reqBytes: into.reqBytes + add.reqBytes,
115198
+ resMessages: into.resMessages + add.resMessages,
115199
+ resBytes: into.resBytes + add.resBytes,
115200
+ evtMessages: into.evtMessages + add.evtMessages,
115201
+ evtBytes: into.evtBytes + add.evtBytes
115202
+ };
115203
+ }
115204
+ function peerMessages(delta) {
115205
+ return socketDirectionMessages(delta.tx) + socketDirectionMessages(delta.rx);
115206
+ }
115207
+ function diffSocketPlane(baseline, current, topN = 5) {
115208
+ const deltas = [];
115209
+ let tx = EMPTY_SOCKET_DIRECTION;
115210
+ let rx = EMPTY_SOCKET_DIRECTION;
115211
+ for (const sample of current.peers) {
115212
+ const before = baseline.peers.get(sample.peerId);
115213
+ const delta = {
115214
+ peerId: sample.peerId,
115215
+ tx: diffDirection(sample.tx, before?.tx),
115216
+ rx: diffDirection(sample.rx, before?.rx)
115217
+ };
115218
+ tx = addDirection(tx, delta.tx);
115219
+ rx = addDirection(rx, delta.rx);
115220
+ if (peerMessages(delta) > 0) deltas.push(delta);
115221
+ }
115222
+ return {
115223
+ peersConnected: current.peersConnected,
115224
+ peersMeasured: current.peers.length,
115225
+ tx,
115226
+ rx,
115227
+ top: deltas.toSorted((a, b) => peerMessages(b) - peerMessages(a) || a.peerId.localeCompare(b.peerId)).slice(0, topN)
115228
+ };
115229
+ }
115230
+ function createSocketPlaneMeter(reader, topN = 5) {
115231
+ let baseline = EMPTY_SOCKET_PLANE_BASELINE;
115232
+ return { read: () => {
115233
+ const current = reader();
115234
+ if (current === void 0) return void 0;
115235
+ const window2 = diffSocketPlane(baseline, current, topN);
115236
+ baseline = { peers: new Map(current.peers.map((p) => [p.peerId, p])) };
115237
+ return window2;
115238
+ } };
115239
+ }
115240
+ function kb(bytes) {
115241
+ return Math.round(bytes / 1024);
115242
+ }
115243
+ function formatDirection(sample) {
115244
+ return `req:${sample.reqMessages}/${kb(sample.reqBytes)}kB,res:${sample.resMessages}/${kb(sample.resBytes)}kB,evt:${sample.evtMessages}/${kb(sample.evtBytes)}kB`;
115245
+ }
115246
+ function formatPeer(delta) {
115247
+ return `${delta.peerId}:tx=${delta.tx.reqMessages}/${delta.tx.resMessages}/${delta.tx.evtMessages}:rx=${delta.rx.reqMessages}/${delta.rx.resMessages}/${delta.rx.evtMessages}:kB=${kb(socketDirectionBytes(delta.tx))}/${kb(socketDirectionBytes(delta.rx))}`;
115248
+ }
115249
+ function formatSocketPlane(window2) {
115250
+ if (window2 === void 0) return "";
115251
+ const top = window2.top.map(formatPeer).join(",");
115252
+ return ` socketPeers=${window2.peersMeasured}/${window2.peersConnected} socketTx=${formatDirection(window2.tx)} socketRx=${formatDirection(window2.rx)}` + (top.length === 0 ? "" : ` socketTop=${top}`);
115253
+ }
114758
115254
  var DECISIVE_HEAP_SPACES = ["old_space", "large_object_space"];
114759
115255
  var HEAP_SPACE_REPORT_MIN_MB = 32;
114760
115256
  var BYTES_PER_MB = 1048576;
@@ -114968,6 +115464,7 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
114968
115464
  function startHeapWatch(label = HUB_MAIN_HEAP_WATCH_LABEL, sink = consoleSink, intervalMs = HEAP_WATCH_INTERVAL_MS, reclaimOptions, loopDelay = createLoopDelayMeter(), execArgv = process.execArgv, announceCeilingOrigin = true, rssBudget, probes) {
114969
115465
  const readSpaces = probes?.readSpaces ?? readHeapSpaces;
114970
115466
  const eventPlane = probes?.eventPlane === void 0 ? void 0 : createEventPlaneMeter(probes.eventPlane);
115467
+ const socketPlane = probes?.socketPlane === void 0 ? void 0 : createSocketPlaneMeter(probes.socketPlane);
114971
115468
  const readMemory = reclaimOptions?.readMemory ?? (() => process.memoryUsage());
114972
115469
  const now = reclaimOptions?.now ?? (() => Date.now());
114973
115470
  const triggerMb = reclaimOptions?.triggerMb ?? 1024;
@@ -115003,7 +115500,7 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
115003
115500
  return;
115004
115501
  }
115005
115502
  const after = read();
115006
- sink.info(`[mem] reclaim ${label} stranded=${strandedMb(sample)}MB rss=${sample.rssMb}MB\u2192${after.rssMb}MB freed=${sample.rssMb - after.rssMb}MB arrayBuffers=${sample.arrayBuffersMb}MB\u2192${after.arrayBuffersMb}MB took=${now() - startedAt}ms`);
115503
+ sink.info(`[mem] reclaim ${label} stranded=${strandedMb(sample)}MB rss=${sample.rssMb}MB\u2192${after.rssMb}MB freed=${sample.rssMb - after.rssMb}MB arrayBuffers=${sample.arrayBuffersMb}MB\u2192${after.arrayBuffersMb}MB took=${now() - startedAt}ms heapUsed=${sample.heapUsedMb}MB\u2192${after.heapUsedMb}MB`);
115007
115504
  if (passesAtFloor >= 6 && !steadyStateAnnounced) {
115008
115505
  steadyStateAnnounced = true;
115009
115506
  const nextFloorMs = reclaimIntervalMs(passesAtFloor, minIntervalMs, steadyStateIntervalMs);
@@ -115045,7 +115542,7 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
115045
115542
  const due = at - lastLoggedAt >= intervalMs;
115046
115543
  if (mode === "escalated" || due) {
115047
115544
  lastLoggedAt = at;
115048
- const line = format2(label, sample, loopDelay?.read(), budgetMb, `${formatHeapSpaces(readSpaces())}${formatEventPlane(eventPlane?.read())}`);
115545
+ const line = format2(label, sample, loopDelay?.read(), budgetMb, `${formatHeapSpaces(readSpaces())}${formatEventPlane(eventPlane?.read())}` + formatSocketPlane(socketPlane?.read()));
115049
115546
  if (sample.nearLimit) sink.warn(`${line} \u2014 APPROACHING HEAP LIMIT`);
115050
115547
  else if (mode === "escalated") sink.warn(`${line} \u2014 heap elevated, sampling every ${probeIntervalMs}ms`);
115051
115548
  else sink.info(line);
@@ -118259,6 +118756,33 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
118259
118756
  chunks = [];
118260
118757
  /** Sum of `chunks[i].byteLength` — tracked so length checks cost nothing. */
118261
118758
  buffered = 0;
118759
+ /** Backing store for {@link lastFrameBytes}; see that getter. */
118760
+ sizes = [];
118761
+ /**
118762
+ * On-the-wire byte length (4-byte prefix included) of each frame returned by
118763
+ * the most recent {@link push}, index-aligned with the returned array.
118764
+ *
118765
+ * It exists because the frames come back DECODED, and a decoded value has no
118766
+ * memory of how many bytes it cost — so a caller counting received bytes per
118767
+ * message kind (`SocketChannel`, feeding the `[mem]` line's per-peer socket
118768
+ * attribution) could otherwise only charge a whole `data` chunk, which may
118769
+ * hold several frames of different kinds, or half of one.
118770
+ *
118771
+ * **Only the first `frames.length` entries are meaningful**, and the array is
118772
+ * never truncated. That is deliberate and it was measured on Node 24.17:
118773
+ * `sizes.length = 0` + `push` costs **35.1–35.9 ns** per call — V8 shrinks the
118774
+ * backing store and the next call re-grows it — which is 3 % of
118775
+ * `FrameDecoder.push` itself and 15× the counter it feeds. Writing in place
118776
+ * costs **0.06–0.13 ns**. On a path that runs ~22 000 times a second, an
118777
+ * instrument may not be the most expensive thing on the line it measures.
118778
+ *
118779
+ * Read it immediately after the `push` that produced it — it is a window onto
118780
+ * the decoder, not a value. Safe because `push` is only ever driven by a
118781
+ * socket `data` event, which Node never delivers re-entrantly.
118782
+ */
118783
+ get lastFrameBytes() {
118784
+ return this.sizes;
118785
+ }
118262
118786
  push(chunk) {
118263
118787
  if (chunk.byteLength > 0) {
118264
118788
  this.chunks.push(chunk);
@@ -118276,6 +118800,7 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
118276
118800
  body = Buffer.allocUnsafeSlow(len);
118277
118801
  frame.copy(body, 0, HEADER_BYTES);
118278
118802
  } else body = frame.subarray(HEADER_BYTES);
118803
+ this.sizes[frames.length] = total;
118279
118804
  frames.push(decode(body));
118280
118805
  }
118281
118806
  return frames;
@@ -118355,6 +118880,11 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
118355
118880
  };
118356
118881
  closed = false;
118357
118882
  closeFired = false;
118883
+ /** Frames written to the peer, cumulative, split by kind. See
118884
+ * {@link readTraffic} and `transport/socket-traffic.ts`. */
118885
+ txCounters = createSocketDirectionCounters();
118886
+ /** Frames read from the peer, cumulative, split by kind. */
118887
+ rxCounters = createSocketDirectionCounters();
118358
118888
  constructor(socket) {
118359
118889
  this.socket = socket;
118360
118890
  socket.on("data", (chunk) => this.onData(chunk));
@@ -118422,14 +118952,41 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
118422
118952
  this.closed = true;
118423
118953
  this.socket.destroy();
118424
118954
  }
118955
+ /**
118956
+ * This channel's cumulative traffic, both directions, split by frame kind.
118957
+ *
118958
+ * Cumulative for the life of the channel: the reporting layer subtracts the
118959
+ * previous reading, because the question is a RATE ("which peer receives the
118960
+ * 22 500 writes/s") and not a total since boot.
118961
+ */
118962
+ readTraffic() {
118963
+ return {
118964
+ tx: sampleSocketDirection(this.txCounters),
118965
+ rx: sampleSocketDirection(this.rxCounters)
118966
+ };
118967
+ }
118425
118968
  send(frame) {
118426
118969
  if (this.closed) return;
118427
- this.socket.write(encodeFrame(frame));
118970
+ const encoded = encodeFrame(frame);
118971
+ recordSocketFrame(this.txCounters, frame.k, encoded.byteLength);
118972
+ this.socket.write(encoded);
118428
118973
  }
118429
118974
  onData(chunk) {
118430
- for (const f of this.decoder.push(chunk)) this.handleFrame(f);
118975
+ const frames = this.decoder.push(chunk);
118976
+ const sizes = this.decoder.lastFrameBytes;
118977
+ for (let i = 0; i < frames.length; i += 1) this.handleFrame(frames[i], sizes[i] ?? 0);
118431
118978
  }
118432
- async handleFrame(frame) {
118979
+ /**
118980
+ * `wireBytes` is the frame's on-the-wire length, counted HERE rather than in
118981
+ * `onData` on purpose: a peer that sends a frame this decoder can parse but
118982
+ * whose shape is not a `Frame` throws on `frame.k`, and inside this async
118983
+ * method that stays the rejected promise it has always been. Reading `.k`
118984
+ * one frame earlier, synchronously in the socket's `data` handler, would
118985
+ * turn the same malformed frame into an uncaught exception on the transport
118986
+ * — a diagnostic that can crash the process it watches.
118987
+ */
118988
+ async handleFrame(frame, wireBytes) {
118989
+ recordSocketFrame(this.rxCounters, frame.k, wireBytes);
118433
118990
  if (frame.k === "req") {
118434
118991
  try {
118435
118992
  const result = await this.requestHandler(frame.body);
@@ -119150,6 +119707,22 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
119150
119707
  };
119151
119708
  }
119152
119709
  /**
119710
+ * This child's socket traffic — bytes and messages, both directions, split by
119711
+ * frame kind — or `null` when the child is gone or its channel keeps no
119712
+ * counters (an in-process or test transport).
119713
+ *
119714
+ * The event counters above cover ONE kind of frame and, once surfaced on
119715
+ * 2026-08-29, accounted for 4% of hub-main's ~44 000 socket syscalls/s. This
119716
+ * is the same question asked of every frame the channel moves, and it is the
119717
+ * only per-peer attribution that exists: `/proc` reports one `rchar`/`wchar`
119718
+ * pair for the whole process and has no per-socket breakdown.
119719
+ */
119720
+ getChildSocketTraffic(childId) {
119721
+ const entry = this.children.get(childId);
119722
+ if (entry === void 0) return null;
119723
+ return entry.channel.readTraffic?.() ?? null;
119724
+ }
119725
+ /**
119153
119726
  * The regime the counters above were produced under, read once from
119154
119727
  * `CAMSTACK_UDS_EVENT_FANOUT` at construction.
119155
119728
  *
@@ -121645,6 +122218,18 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
121645
122218
  return DeviceRegistry;
121646
122219
  }
121647
122220
  });
122221
+ Object.defineProperty(exports, "EMPTY_SOCKET_DIRECTION", {
122222
+ enumerable: true,
122223
+ get: function() {
122224
+ return EMPTY_SOCKET_DIRECTION;
122225
+ }
122226
+ });
122227
+ Object.defineProperty(exports, "EMPTY_SOCKET_PLANE_BASELINE", {
122228
+ enumerable: true,
122229
+ get: function() {
122230
+ return EMPTY_SOCKET_PLANE_BASELINE;
122231
+ }
122232
+ });
121648
122233
  Object.defineProperty(exports, "EVENT_PLANE_TOP_N", {
121649
122234
  enumerable: true,
121650
122235
  get: function() {
@@ -121777,6 +122362,12 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
121777
122362
  return RUNNER_RSS_BUDGET_ENV;
121778
122363
  }
121779
122364
  });
122365
+ Object.defineProperty(exports, "SOCKET_PLANE_TOP_N", {
122366
+ enumerable: true,
122367
+ get: function() {
122368
+ return SOCKET_PLANE_TOP_N;
122369
+ }
122370
+ });
121780
122371
  Object.defineProperty(exports, "SocketChannel", {
121781
122372
  enumerable: true,
121782
122373
  get: function() {
@@ -121975,6 +122566,24 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
121975
122566
  return createParentUnownedCallHandler;
121976
122567
  }
121977
122568
  });
122569
+ Object.defineProperty(exports, "createSocketDirectionCounters", {
122570
+ enumerable: true,
122571
+ get: function() {
122572
+ return createSocketDirectionCounters;
122573
+ }
122574
+ });
122575
+ Object.defineProperty(exports, "createSocketPlaneMeter", {
122576
+ enumerable: true,
122577
+ get: function() {
122578
+ return createSocketPlaneMeter;
122579
+ }
122580
+ });
122581
+ Object.defineProperty(exports, "createSocketPlaneReader", {
122582
+ enumerable: true,
122583
+ get: function() {
122584
+ return createSocketPlaneReader;
122585
+ }
122586
+ });
121978
122587
  Object.defineProperty(exports, "createUdsAddonContext", {
121979
122588
  enumerable: true,
121980
122589
  get: function() {
@@ -122029,6 +122638,12 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
122029
122638
  return diffEventPlane;
122030
122639
  }
122031
122640
  });
122641
+ Object.defineProperty(exports, "diffSocketPlane", {
122642
+ enumerable: true,
122643
+ get: function() {
122644
+ return diffSocketPlane;
122645
+ }
122646
+ });
122032
122647
  Object.defineProperty(exports, "emitHeapDiagnosticReport", {
122033
122648
  enumerable: true,
122034
122649
  get: function() {
@@ -122053,6 +122668,12 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
122053
122668
  return formatHeapSpaces;
122054
122669
  }
122055
122670
  });
122671
+ Object.defineProperty(exports, "formatSocketPlane", {
122672
+ enumerable: true,
122673
+ get: function() {
122674
+ return formatSocketPlane;
122675
+ }
122676
+ });
122056
122677
  Object.defineProperty(exports, "getBrokerEventBus", {
122057
122678
  enumerable: true,
122058
122679
  get: function() {
@@ -122191,6 +122812,12 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
122191
122812
  return reclaimIntervalMs;
122192
122813
  }
122193
122814
  });
122815
+ Object.defineProperty(exports, "recordSocketFrame", {
122816
+ enumerable: true,
122817
+ get: function() {
122818
+ return recordSocketFrame;
122819
+ }
122820
+ });
122194
122821
  Object.defineProperty(exports, "registerEventBusService", {
122195
122822
  enumerable: true,
122196
122823
  get: function() {
@@ -122221,6 +122848,12 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
122221
122848
  return runNpm;
122222
122849
  }
122223
122850
  });
122851
+ Object.defineProperty(exports, "sampleSocketDirection", {
122852
+ enumerable: true,
122853
+ get: function() {
122854
+ return sampleSocketDirection;
122855
+ }
122856
+ });
122224
122857
  Object.defineProperty(exports, "selectReportedSpaces", {
122225
122858
  enumerable: true,
122226
122859
  get: function() {
@@ -122257,6 +122890,18 @@ var require_manifest_python_deps_COeSr7el = __commonJS({
122257
122890
  return shouldReclaim;
122258
122891
  }
122259
122892
  });
122893
+ Object.defineProperty(exports, "socketDirectionBytes", {
122894
+ enumerable: true,
122895
+ get: function() {
122896
+ return socketDirectionBytes;
122897
+ }
122898
+ });
122899
+ Object.defineProperty(exports, "socketDirectionMessages", {
122900
+ enumerable: true,
122901
+ get: function() {
122902
+ return socketDirectionMessages;
122903
+ }
122904
+ });
122260
122905
  Object.defineProperty(exports, "startHeapWatch", {
122261
122906
  enumerable: true,
122262
122907
  get: function() {
@@ -126095,7 +126740,7 @@ var require_dist3 = __commonJS({
126095
126740
  "use strict";
126096
126741
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
126097
126742
  var require_chunk = require_chunk_Cek0wNdY();
126098
- var require_dist10 = require_dist_BPlfW_CG();
126743
+ var require_dist10 = require_dist_CDgIzo82();
126099
126744
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
126100
126745
  require_alerts();
126101
126746
  var require_formatter = require_formatter_DqAKDlvN();
@@ -126121,7 +126766,7 @@ var require_dist3 = __commonJS({
126121
126766
  var require_builtins_winston_logging_index = require_winston_logging();
126122
126767
  var require_file_data_plane = require_file_data_plane_DO8KbxCe();
126123
126768
  var require_tls$1 = require_tls_BxQlomxd();
126124
- var require_manifest_python_deps = require_manifest_python_deps_COeSr7el();
126769
+ var require_manifest_python_deps = require_manifest_python_deps_DVODn_qc();
126125
126770
  var require_resource_monitor = require_resource_monitor_CdnzxBLP();
126126
126771
  var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
126127
126772
  var zod = require_zod();
@@ -206502,6 +207147,8 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
206502
207147
  exports.DeviceManagerAddon = require_builtins_device_manager_device_manager_addon.DeviceManagerAddon;
206503
207148
  exports.DeviceRegistry = require_manifest_python_deps.DeviceRegistry;
206504
207149
  exports.DeviceStore = require_builtins_sqlite_storage_index.DeviceStore$1;
207150
+ exports.EMPTY_SOCKET_DIRECTION = require_manifest_python_deps.EMPTY_SOCKET_DIRECTION;
207151
+ exports.EMPTY_SOCKET_PLANE_BASELINE = require_manifest_python_deps.EMPTY_SOCKET_PLANE_BASELINE;
206505
207152
  exports.EVENT_PLANE_TOP_N = require_manifest_python_deps.EVENT_PLANE_TOP_N;
206506
207153
  exports.EVENT_TOPIC_PREFIX = require_manifest_python_deps.EVENT_TOPIC_PREFIX;
206507
207154
  exports.EngineManagerResolver = EngineManagerResolver;
@@ -206575,6 +207222,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
206575
207222
  exports.ReplEngine = ReplEngine;
206576
207223
  exports.RingBuffer = RingBuffer;
206577
207224
  exports.SERVER_AUTH_OID = require_tls$1.SERVER_AUTH_OID;
207225
+ exports.SOCKET_PLANE_TOP_N = require_manifest_python_deps.SOCKET_PLANE_TOP_N;
206578
207226
  exports.ScopedLogger = ScopedLogger;
206579
207227
  exports.ScopedTokenManager = require_builtins_local_auth_local_auth_addon.ScopedTokenManager;
206580
207228
  exports.SocketChannel = require_manifest_python_deps.SocketChannel;
@@ -206660,6 +207308,9 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
206660
207308
  exports.createReadinessService = createReadinessService;
206661
207309
  exports.createReadinessServiceForRegistry = createReadinessServiceForRegistry;
206662
207310
  exports.createScopedProcessManager = createScopedProcessManager;
207311
+ exports.createSocketDirectionCounters = require_manifest_python_deps.createSocketDirectionCounters;
207312
+ exports.createSocketPlaneMeter = require_manifest_python_deps.createSocketPlaneMeter;
207313
+ exports.createSocketPlaneReader = require_manifest_python_deps.createSocketPlaneReader;
206663
207314
  exports.createStreamProbeBrokerService = createStreamProbeBrokerService;
206664
207315
  exports.createUdsAddonContext = require_manifest_python_deps.createUdsAddonContext;
206665
207316
  exports.createUdsEventBridge = require_manifest_python_deps.createUdsEventBridge;
@@ -206673,6 +207324,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
206673
207324
  exports.describeRss = require_manifest_python_deps.describeRss;
206674
207325
  exports.detectWorkspacePackagesDir = detectWorkspacePackagesDir;
206675
207326
  exports.diffEventPlane = require_manifest_python_deps.diffEventPlane;
207327
+ exports.diffSocketPlane = require_manifest_python_deps.diffSocketPlane;
206676
207328
  Object.defineProperty(exports, "downloadBinary", {
206677
207329
  enumerable: true,
206678
207330
  get: function() {
@@ -206720,6 +207372,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
206720
207372
  exports.formatEventPlane = require_manifest_python_deps.formatEventPlane;
206721
207373
  exports.formatHeapSpaces = require_manifest_python_deps.formatHeapSpaces;
206722
207374
  exports.formatLogLine = require_formatter.formatLogLine;
207375
+ exports.formatSocketPlane = require_manifest_python_deps.formatSocketPlane;
206723
207376
  exports.getBrokerEventBus = require_manifest_python_deps.getBrokerEventBus;
206724
207377
  exports.getCapUsageRegistry = require_manifest_python_deps.getCapUsageRegistry;
206725
207378
  Object.defineProperty(exports, "getFfmpegDownloadUrl", {
@@ -206799,6 +207452,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
206799
207452
  exports.readTlsMode = require_tls$1.readTlsMode;
206800
207453
  exports.readinessKey = require_dist10.readinessKey;
206801
207454
  exports.reclaimIntervalMs = require_manifest_python_deps.reclaimIntervalMs;
207455
+ exports.recordSocketFrame = require_manifest_python_deps.recordSocketFrame;
206802
207456
  exports.registerEventBusService = require_manifest_python_deps.registerEventBusService;
206803
207457
  exports.registerLanHttpHandler = require_tls$1.registerLanHttpHandler;
206804
207458
  exports.reissueTlsLeaf = require_tls$1.reissueTlsLeaf;
@@ -206807,6 +207461,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
206807
207461
  exports.resolveNpmInvocation = require_manifest_python_deps.resolveNpmInvocation;
206808
207462
  exports.runHubAddonBoot = runHubAddonBoot;
206809
207463
  exports.runNpm = require_manifest_python_deps.runNpm;
207464
+ exports.sampleSocketDirection = require_manifest_python_deps.sampleSocketDirection;
206810
207465
  exports.scheduleSelfRestart = scheduleSelfRestart;
206811
207466
  exports.scopeKey = require_dist10.scopeKey;
206812
207467
  exports.scopesAllowAddon = require_dist10.scopesAllowAddon;
@@ -206817,6 +207472,8 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
206817
207472
  exports.setHubConnected = require_manifest_python_deps.setHubConnected;
206818
207473
  exports.setNodeEventInterest = require_manifest_python_deps.setNodeEventInterest;
206819
207474
  exports.shouldReclaim = require_manifest_python_deps.shouldReclaim;
207475
+ exports.socketDirectionBytes = require_manifest_python_deps.socketDirectionBytes;
207476
+ exports.socketDirectionMessages = require_manifest_python_deps.socketDirectionMessages;
206820
207477
  exports.startHeapWatch = require_manifest_python_deps.startHeapWatch;
206821
207478
  exports.startRunnerHeapWatch = require_manifest_python_deps.startRunnerHeapWatch;
206822
207479
  exports.strandedMb = require_manifest_python_deps.strandedMb;
@@ -206858,7 +207515,7 @@ var require_dist4 = __commonJS({
206858
207515
  "use strict";
206859
207516
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
206860
207517
  var require_event_category = require_event_category_BaEgqJNv();
206861
- var require_sleep = require_sleep_DUxF5DdC();
207518
+ var require_sleep = require_sleep_CJrvRDlD();
206862
207519
  var require_canonical_hash = require_canonical_hash_DNV8S5ET();
206863
207520
  var require_enums2 = require_enums();
206864
207521
  var require_err_msg = require_err_msg_COpsHMw2();
@@ -208179,29 +208836,50 @@ var require_dist4 = __commonJS({
208179
208836
  });
208180
208837
  var StorageMigrationLeaseInputSchema = zod.z.object({ leaseId: zod.z.string().min(1) });
208181
208838
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: zod.z.string().min(1) });
208839
+ var MediaRelocateModeSchema = zod.z.enum([
208840
+ "move",
208841
+ "seal",
208842
+ "gallery"
208843
+ ]);
208182
208844
  var RelocateMediaInputSchema = zod.z.object({
208183
208845
  toLocationId: zod.z.string(),
208184
- throttleMbps: zod.z.number().min(1).max(1e3).optional()
208846
+ throttleMbps: zod.z.number().min(1).max(1e3).optional(),
208847
+ /** Omitted = `move`, the pre-existing behaviour. */
208848
+ mode: MediaRelocateModeSchema.optional()
208849
+ });
208850
+ var UnstampedEventMediaCountSchema = zod.z.object({
208851
+ media: zod.z.number().int().nonnegative(),
208852
+ retrainFrames: zod.z.number().int().nonnegative(),
208853
+ total: zod.z.number().int().nonnegative()
208185
208854
  });
208186
208855
  var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: zod.z.string().min(1) });
208187
208856
  var StorageMigrationClassSchema = zod.z.enum([
208188
208857
  "recordings",
208189
208858
  "recordingsLow",
208190
- "eventMedia"
208859
+ "eventMedia",
208860
+ "backups",
208861
+ "galleryMedia"
208191
208862
  ]);
208192
208863
  var StorageMigrationDestinationsSchema = zod.z.object({
208193
208864
  recordings: zod.z.string().min(1).optional(),
208194
208865
  recordingsLow: zod.z.string().min(1).optional(),
208195
- eventMedia: zod.z.string().min(1).optional()
208866
+ eventMedia: zod.z.string().min(1).optional(),
208867
+ backups: zod.z.string().min(1).optional(),
208868
+ galleryMedia: zod.z.string().min(1).optional()
208196
208869
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
208870
+ var StorageMigrationModeSchema = zod.z.enum(["blocking", "nonBlocking"]);
208197
208871
  var StorageMigrationInputSchema = zod.z.object({
208198
208872
  destinations: StorageMigrationDestinationsSchema,
208199
- throttleMbps: zod.z.number().min(1).max(1e3).optional()
208873
+ throttleMbps: zod.z.number().min(1).max(1e3).optional(),
208874
+ /** Omitted = `blocking`, which stays the default. */
208875
+ mode: StorageMigrationModeSchema.optional()
208200
208876
  });
208201
208877
  var StorageMigrationPhaseSchema = zod.z.enum([
208202
208878
  "planning",
208879
+ "sealing",
208203
208880
  "pausing",
208204
208881
  "moving",
208882
+ "draining",
208205
208883
  "verifying",
208206
208884
  "repointing",
208207
208885
  "refreshing",
@@ -208226,6 +208904,9 @@ var require_dist4 = __commonJS({
208226
208904
  var StorageMigrationJobSchema = zod.z.object({
208227
208905
  jobId: zod.z.string(),
208228
208906
  phase: StorageMigrationPhaseSchema,
208907
+ /** Which order this job is running. `status` is the only place an operator
208908
+ * can tell a seconds-long cutover from a thirty-hour one. */
208909
+ mode: StorageMigrationModeSchema,
208229
208910
  destinations: StorageMigrationDestinationsSchema,
208230
208911
  throttleMbps: zod.z.number(),
208231
208912
  moves: zod.z.array(StorageMigrationMoveSchema),
@@ -208238,13 +208919,31 @@ var require_dist4 = __commonJS({
208238
208919
  finishedAt: zod.z.number().nullable(),
208239
208920
  error: zod.z.string().nullable()
208240
208921
  });
208922
+ var StorageMigrationFindingCodeSchema = zod.z.enum([
208923
+ "sharesDeviceWithSource",
208924
+ "deviceIdentityUnknown",
208925
+ "unstampedEventMediaRows",
208926
+ "blockingOnly",
208927
+ "noMover"
208928
+ ]);
208929
+ var StorageMigrationFindingSchema = zod.z.object({
208930
+ code: StorageMigrationFindingCodeSchema,
208931
+ storageClass: StorageMigrationClassSchema,
208932
+ /** Human-readable, already carrying the ids and counts. */
208933
+ message: zod.z.string()
208934
+ });
208241
208935
  var StorageMigrationPlanSchema = zod.z.object({
208242
208936
  destinations: StorageMigrationDestinationsSchema,
208937
+ /** The mode this plan was built for. A plan is only valid for its mode: the
208938
+ * `eventMedia` seal gate and the single-cardinality refusal both depend on
208939
+ * it. */
208940
+ mode: StorageMigrationModeSchema,
208243
208941
  moves: zod.z.array(zod.z.object({
208244
208942
  storageClass: StorageMigrationClassSchema,
208245
208943
  fromLocationId: zod.z.string(),
208246
208944
  toLocationId: zod.z.string()
208247
- }))
208945
+ })),
208946
+ findings: zod.z.array(StorageMigrationFindingSchema)
208248
208947
  });
208249
208948
  var SUB_DETECTION_TYPES = ["face", "plate"];
208250
208949
  var RECOGNITION_TYPES = [
@@ -221220,6 +221919,17 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
221220
221919
  kind: "mutation",
221221
221920
  auth: "admin"
221222
221921
  }),
221922
+ /**
221923
+ * How many media / retrain rows still carry NO `locationId`.
221924
+ *
221925
+ * A NULL row means "wherever `eventMedia` points NOW", so the instant a
221926
+ * repoint moves that pointer every such row reads from the new disk while
221927
+ * its bytes are on the old one — the archive goes dark until a drain
221928
+ * happens to stamp it. This count is what the migration planner's
221929
+ * non-blocking gate reads; `relocateMedia({ mode: 'seal' })` is what drives
221930
+ * it to zero.
221931
+ */
221932
+ countUnstampedEventMedia: require_sleep.method(zod.z.object({}), UnstampedEventMediaCountSchema, { auth: "admin" }),
221223
221933
  /** Every relocate job this addon knows about, newest first (in RAM: the
221224
221934
  * move is resumable, so a lost list costs nothing but the display). */
221225
221935
  listRelocateMediaJobs: require_sleep.method(zod.z.object({}), zod.z.array(RelocateJobSchema).readonly(), {
@@ -223836,7 +224546,24 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
223836
224546
  kind: "mutation",
223837
224547
  auth: "admin"
223838
224548
  }),
223839
- deleteLocation: require_sleep.method(zod.z.object({ id: zod.z.string() }), zod.z.void(), {
224549
+ /**
224550
+ * Remove a location record. REFUSES a location that still holds data —
224551
+ * deleting a drained location whose durable rows still name it is how this
224552
+ * hub acquired 2 131 ghost `recordings:high` segments, and playback does
224553
+ * not stat, so the operator sees a silent black window rather than an
224554
+ * error.
224555
+ *
224556
+ * `force` exists because the occupancy check is BEST-EFFORT and refuses on
224557
+ * "unknown" as well as on "occupied" (a read that fails must not authorise
224558
+ * a destruction — D49). A location on a removed disk, on another node, or
224559
+ * behind a remote provider answers "unknown" forever, and a refusal an
224560
+ * operator cannot override is its own failure mode. `force: true` is
224561
+ * logged, loudly, with what the check saw.
224562
+ */
224563
+ deleteLocation: require_sleep.method(zod.z.object({
224564
+ id: zod.z.string(),
224565
+ force: zod.z.boolean().optional()
224566
+ }), zod.z.void(), {
223840
224567
  kind: "mutation",
223841
224568
  auth: "admin"
223842
224569
  }),
@@ -239875,6 +240602,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
239875
240602
  addonId: null,
239876
240603
  access: "create"
239877
240604
  },
240605
+ "pipelineAnalytics.countUnstampedEventMedia": {
240606
+ capName: "pipeline-analytics",
240607
+ capScope: "device",
240608
+ addonId: null,
240609
+ access: "view"
240610
+ },
239878
240611
  "pipelineAnalytics.deleteDeviceEvents": {
239879
240612
  capName: "pipeline-analytics",
239880
240613
  capScope: "device",
@@ -248898,6 +249631,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
248898
249631
  exports.MediaPlayerRepeatSchema = MediaPlayerRepeatSchema;
248899
249632
  exports.MediaPlayerStateSchema = MediaPlayerStateSchema;
248900
249633
  exports.MediaPlayerStatusSchema = MediaPlayerStatusSchema;
249634
+ exports.MediaRelocateModeSchema = MediaRelocateModeSchema;
248901
249635
  exports.MeshPeerSchema = MeshPeerSchema;
248902
249636
  exports.MeshStatusSchema = MeshStatusSchema;
248903
249637
  exports.MethodAccessSchema = MethodAccessSchema;
@@ -249275,11 +250009,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
249275
250009
  exports.StorageLocationTypeSchema = StorageLocationTypeSchema;
249276
250010
  exports.StorageMigrationClassSchema = StorageMigrationClassSchema;
249277
250011
  exports.StorageMigrationDestinationsSchema = StorageMigrationDestinationsSchema;
250012
+ exports.StorageMigrationFindingCodeSchema = StorageMigrationFindingCodeSchema;
250013
+ exports.StorageMigrationFindingSchema = StorageMigrationFindingSchema;
249278
250014
  exports.StorageMigrationFootageMoveInputSchema = StorageMigrationFootageMoveInputSchema;
249279
250015
  exports.StorageMigrationInputSchema = StorageMigrationInputSchema;
249280
250016
  exports.StorageMigrationJobSchema = StorageMigrationJobSchema;
249281
250017
  exports.StorageMigrationLeaseInputSchema = StorageMigrationLeaseInputSchema;
249282
250018
  exports.StorageMigrationMediaMoveInputSchema = StorageMigrationMediaMoveInputSchema;
250019
+ exports.StorageMigrationModeSchema = StorageMigrationModeSchema;
249283
250020
  exports.StorageMigrationMoveSchema = StorageMigrationMoveSchema;
249284
250021
  exports.StorageMigrationParticipantSchema = StorageMigrationParticipantSchema;
249285
250022
  exports.StorageMigrationPhaseSchema = StorageMigrationPhaseSchema;
@@ -249355,6 +250092,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
249355
250092
  exports.UNIT_TABLE = UNIT_TABLE;
249356
250093
  exports.UnifiedBrokerInfoSchema = BrokerInfoSchema$1;
249357
250094
  exports.UnitConversionError = UnitConversionError;
250095
+ exports.UnstampedEventMediaCountSchema = UnstampedEventMediaCountSchema;
249358
250096
  exports.UpdateIntegrationInputSchema = UpdateIntegrationInputSchema;
249359
250097
  exports.UpdateStatusSchema = UpdateStatusSchema;
249360
250098
  exports.UpdateUserInputSchema = UpdateUserInputSchema;
@@ -421823,6 +422561,17 @@ var require_main4 = __commonJS({
421823
422561
  return broker === void 0 ? null : (0, system_1.getMoleculerEventStats)(broker);
421824
422562
  },
421825
422563
  crossNodeMode: () => (0, system_1.readMoleculerFanoutMode)()
422564
+ }),
422565
+ // The fan-out counters above answered their own question and sharpened
422566
+ // this one: the event plane is 4% of this process's ~44 000 socket
422567
+ // syscalls/s. The other 96% had no instrument — `/proc` reports one
422568
+ // rchar/wchar pair per PROCESS and has no per-socket breakdown, so the
422569
+ // best attribution available was a correlation with `hub/pipeline-
422570
+ // analytics` computed from a runner whose rchar also counts page-cache
422571
+ // reads. These counters live on the channel itself, so the attribution is
422572
+ // per peer and per frame kind. Same lazy registry, same reason.
422573
+ socketPlane: (0, system_1.createSocketPlaneReader)({
422574
+ registry: () => moleculerForEventPlane?.childRegistry ?? null
421826
422575
  })
421827
422576
  });
421828
422577
  cleanupOrphanProcesses();