camstack 1.2.145 → 1.2.146

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-B84YlKAH.js
23637
- var require_dist_B84YlKAH = __commonJS({
23638
- "../system/dist/dist-B84YlKAH.js"(exports) {
23636
+ // ../system/dist/dist-DUSfUAgz.js
23637
+ var require_dist_DUSfUAgz = __commonJS({
23638
+ "../system/dist/dist-DUSfUAgz.js"(exports) {
23639
23639
  "use strict";
23640
23640
  var zod = require_zod();
23641
23641
  var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
@@ -25889,7 +25889,23 @@ var require_dist_B84YlKAH = __commonJS({
25889
25889
  "zerolatency",
25890
25890
  "film",
25891
25891
  "animation"
25892
- ]).optional()
25892
+ ]).optional(),
25893
+ /**
25894
+ * ONE slice per access unit.
25895
+ *
25896
+ * `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
25897
+ * the encoder as five NAL slices that share one RTP timestamp and carry one
25898
+ * marker bit. A libwebrtc depacketiser sees five frame-starts and one
25899
+ * frame-end per frame: the first pictures render and the video then freezes
25900
+ * for good while the audio, on its own plane, plays on. Measured on this hub
25901
+ * 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
25902
+ *
25903
+ * A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
25904
+ * `outputArgs` on purpose: an opaque array is part of the sharing key, so two
25905
+ * consumers meaning the same thing spelled differently would stop sharing one
25906
+ * child. Absent means "whatever the encoder does" — today's behaviour.
25907
+ */
25908
+ singleSlicePerFrame: zod.z.boolean().optional()
25893
25909
  });
25894
25910
  var AudioEncodeSchema = zod.z.union([zod.z.literal("passthrough"), zod.z.object({
25895
25911
  codec: zod.z.enum([
@@ -25937,7 +25953,7 @@ var require_dist_B84YlKAH = __commonJS({
25937
25953
  audio: "passthrough"
25938
25954
  };
25939
25955
  ({ ...BASE_LIVE_EGRESS_PROFILE }, { ...BASE_LIVE_EGRESS_PROFILE.video });
25940
- ({ ...BASE_LIVE_EGRESS_PROFILE });
25956
+ ({ ...BASE_LIVE_EGRESS_PROFILE }, { ...BASE_LIVE_EGRESS_PROFILE.video });
25941
25957
  var wiringProbeKindSchema = zod.z.enum([
25942
25958
  "singleton",
25943
25959
  "device",
@@ -38084,13 +38100,91 @@ var require_dist_B84YlKAH = __commonJS({
38084
38100
  deviceIds: zod.z.array(zod.z.number()).min(1),
38085
38101
  since: zod.z.number().optional(),
38086
38102
  until: zod.z.number().optional(),
38087
- limit: zod.z.number().int().min(1).max(200).default(50)
38103
+ limit: zod.z.number().int().min(1).max(200).default(50),
38104
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
38105
+ * meaning of "dog" on every summary surface. */
38106
+ classes: zod.z.array(zod.z.string().min(1).max(60)).max(40).optional()
38107
+ });
38108
+ var SummaryMemberSchema = zod.z.object({
38109
+ trackId: zod.z.string(),
38110
+ /**
38111
+ * The track's class at the time it JOINED, or `null` when it was unresolved
38112
+ * and the track no longer exists to fill it.
38113
+ *
38114
+ * The join, not the latest opinion: class voting can re-decide a track's
38115
+ * class afterwards, and a group's composition is the history of who came
38116
+ * together. A consumer counts these to badge a thumbnail — "two people and a
38117
+ * dog" — rather than being handed a second, denormalised tally that would
38118
+ * drift from the members it claims to describe.
38119
+ */
38120
+ className: zod.z.string().nullable(),
38121
+ /** `primary` is the one member that carries the group's identity. */
38122
+ role: zod.z.enum([
38123
+ "primary",
38124
+ "sibling",
38125
+ "child"
38126
+ ]).nullable(),
38127
+ joinedAt: zod.z.number(),
38128
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
38129
+ * at read time. Absent when nothing resolved, never an empty string. */
38130
+ label: zod.z.string().optional(),
38131
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
38132
+ * recognised nobody, and absent for a member whose track has been pruned —
38133
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
38134
+ subLabel: zod.z.string().optional()
38088
38135
  });
38089
- var SummariesPageSchema = zod.z.object({ summaries: zod.z.array(SummarySchema).readonly() });
38090
38136
  var SummaryDetailSchema = zod.z.object({
38091
38137
  summary: SummarySchema,
38092
- /** Member track ids, in absorption order. */
38093
- trackIds: zod.z.array(zod.z.string()).readonly()
38138
+ /** Members in absorption order, oldest join first. */
38139
+ members: zod.z.array(SummaryMemberSchema).readonly()
38140
+ });
38141
+ var SummaryListItemSchema = zod.z.object({
38142
+ summary: SummarySchema,
38143
+ /** Class counts, most numerous first; `className: null` is a member whose
38144
+ * class never resolved, counted so the counts add up to `memberCount`. */
38145
+ classes: zod.z.array(zod.z.object({
38146
+ className: zod.z.string().nullable(),
38147
+ count: zod.z.number().int().positive()
38148
+ })).readonly()
38149
+ });
38150
+ var SummariesPageSchema = zod.z.object({ summaries: zod.z.array(SummaryListItemSchema).readonly() });
38151
+ var RecentSummariesQueryInput = zod.z.object({
38152
+ /** Devices to merge. An empty array yields an empty page. */
38153
+ deviceIds: zod.z.array(zod.z.number()),
38154
+ /** Window lower bound on `lastActiveAt` (inclusive). */
38155
+ since: zod.z.number().optional(),
38156
+ /** Window upper bound on `lastActiveAt` (inclusive). */
38157
+ until: zod.z.number().optional(),
38158
+ limit: zod.z.number().int().min(1).max(500).default(100),
38159
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
38160
+ cursor: zod.z.string().optional(),
38161
+ /**
38162
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
38163
+ * deliberately the same name: the events filter tree emits one list of ticked
38164
+ * taxonomy nodes, and a summaries feed that called it something else would
38165
+ * make the operator's one filter component produce two shapes.
38166
+ *
38167
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
38168
+ * me the arrivals with a dog" has to return the group of four people who had
38169
+ * one with them; a filter on the group's own fields could only ever answer
38170
+ * about its primary.
38171
+ *
38172
+ * Matched against all THREE tiers of a member — the class (`animal`), the
38173
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
38174
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
38175
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
38176
+ * that read only the class would answer "animal" and never "dog".
38177
+ *
38178
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
38179
+ */
38180
+ classes: zod.z.array(zod.z.string().min(1).max(60)).max(40).optional()
38181
+ });
38182
+ var RecentSummariesPageSchema = zod.z.object({
38183
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
38184
+ * {@link listSummaries} — one list shape, so a card renders the same
38185
+ * whichever feed drew it. */
38186
+ summaries: zod.z.array(SummaryListItemSchema).readonly(),
38187
+ nextCursor: zod.z.string().nullable()
38094
38188
  });
38095
38189
  var RecentTracksPageSchema = zod.z.object({
38096
38190
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -38442,6 +38536,11 @@ var require_dist_B84YlKAH = __commonJS({
38442
38536
  listRecentTracks: method(RecentTracksQueryInput, RecentTracksPageSchema),
38443
38537
  /** Newest first by `openedAt`, for a camera set, inside an optional window. */
38444
38538
  listSummaries: method(SummariesQueryInput, SummariesPageSchema),
38539
+ /**
38540
+ * The PAGED feed — the reel and the Events gallery. Same cursor contract as
38541
+ * {@link listRecentTracks}, so one screen cannot hold two ideas of newest.
38542
+ */
38543
+ listRecentSummaries: method(RecentSummariesQueryInput, RecentSummariesPageSchema),
38445
38544
  /** One summary with its member track ids; `null` for an id nobody wrote. */
38446
38545
  getSummary: method(zod.z.object({ id: zod.z.string() }), SummaryDetailSchema.nullable()),
38447
38546
  clearTracks: method(zod.z.object({ deviceId: zod.z.number() }), zod.z.void(), {
@@ -50683,7 +50782,29 @@ var require_dist_B84YlKAH = __commonJS({
50683
50782
  * disarmed the channels it did not mention would make the Levels page and
50684
50783
  * the Diagnostics page fight over the same value.
50685
50784
  */
50686
- channels: zod.z.array(LogChannelWindowPatchSchema).readonly().optional()
50785
+ channels: zod.z.array(LogChannelWindowPatchSchema).readonly().optional(),
50786
+ /**
50787
+ * How many days the analytics OPS LOG is kept — the audit trail of what
50788
+ * retention actually deleted.
50789
+ *
50790
+ * It lives HERE, on the logging document, and not on the analytics addon's
50791
+ * settings form, on the operator's instruction: every other log-ish window in
50792
+ * this system is configured through this one document, and a retention audit
50793
+ * configured somewhere else is a second place to look when the question is
50794
+ * "what happened to my data".
50795
+ *
50796
+ * That placement has a cost worth stating: this document ships in the
50797
+ * framework closure, so this knob needs a published `@camstack/server` while
50798
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
50799
+ *
50800
+ * `retention-model.ts` argued this one must NOT follow a device window,
50801
+ * because the audit trail is the history of the SWEEP and not of the camera.
50802
+ * That argument stands and is untouched — what changes is that 30 days stops
50803
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
50804
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
50805
+ * state in which the next 9.5 GiB leak cannot be explained.
50806
+ */
50807
+ opsLogRetentionDays: zod.z.number().int().min(0).max(3650).optional()
50687
50808
  });
50688
50809
  var ReportedLoadContributionSchema = LoadContributionSchema.extend({ addonId: zod.z.string() });
50689
50810
  var ReportedFailureContributionSchema = FailureContributionSchema.extend({ addonId: zod.z.string() });
@@ -50722,6 +50843,14 @@ var require_dist_B84YlKAH = __commonJS({
50722
50843
  channels: zod.z.array(LogChannelDescriptorSchema).readonly(),
50723
50844
  /** The channels ARMED right now, each with its deadline. */
50724
50845
  activeChannels: zod.z.array(LogChannelWindowStateSchema).readonly(),
50846
+ /**
50847
+ * The ops-log window in days, as it is in force. `0` = kept forever.
50848
+ *
50849
+ * Reported rather than left to the caller's memory of what it wrote: this is
50850
+ * the value the analytics sweep will actually use, and an operator asking how
50851
+ * long their audit trail lives should not have to infer it from a default.
50852
+ */
50853
+ opsLogRetentionDays: zod.z.number().int().min(0),
50725
50854
  persisted: zod.z.boolean()
50726
50855
  });
50727
50856
  var systemCapability = {
@@ -55851,6 +55980,12 @@ var require_dist_B84YlKAH = __commonJS({
55851
55980
  addonId: null,
55852
55981
  access: "view"
55853
55982
  },
55983
+ "pipelineAnalytics.listRecentSummaries": {
55984
+ capName: "pipeline-analytics",
55985
+ capScope: "device",
55986
+ addonId: null,
55987
+ access: "view"
55988
+ },
55854
55989
  "pipelineAnalytics.listRecentTracks": {
55855
55990
  capName: "pipeline-analytics",
55856
55991
  capScope: "device",
@@ -59678,6 +59813,11 @@ var require_dist_B84YlKAH = __commonJS({
59678
59813
  form: "single",
59679
59814
  optional: true
59680
59815
  }],
59816
+ "pipelineAnalytics.listRecentSummaries": [{
59817
+ name: "deviceIds",
59818
+ form: "array",
59819
+ optional: false
59820
+ }],
59681
59821
  "pipelineAnalytics.listRecentTracks": [{
59682
59822
  name: "deviceIds",
59683
59823
  form: "array",
@@ -61999,7 +62139,7 @@ var require_alerts_addon = __commonJS({
61999
62139
  [Symbol.toStringTag]: { value: "Module" }
62000
62140
  });
62001
62141
  require_chunk_Cek0wNdY();
62002
- var require_dist10 = require_dist_B84YlKAH();
62142
+ var require_dist10 = require_dist_DUSfUAgz();
62003
62143
  function selectExpired(alerts, cutoffMs) {
62004
62144
  return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
62005
62145
  }
@@ -62818,7 +62958,7 @@ var require_console_logging = __commonJS({
62818
62958
  [Symbol.toStringTag]: { value: "Module" }
62819
62959
  });
62820
62960
  require_chunk_Cek0wNdY();
62821
- var require_dist10 = require_dist_B84YlKAH();
62961
+ var require_dist10 = require_dist_DUSfUAgz();
62822
62962
  var require_formatter = require_formatter_DqAKDlvN();
62823
62963
  var LEVEL_RANK = {
62824
62964
  debug: 0,
@@ -62912,7 +63052,7 @@ var require_core_blocks_addon = __commonJS({
62912
63052
  "use strict";
62913
63053
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
62914
63054
  var require_chunk = require_chunk_Cek0wNdY();
62915
- var require_dist10 = require_dist_B84YlKAH();
63055
+ var require_dist10 = require_dist_DUSfUAgz();
62916
63056
  var node_crypto = __require("crypto");
62917
63057
  var node_fs_promises = __require("fs/promises");
62918
63058
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -63809,11 +63949,11 @@ var require_core_blocks = __commonJS({
63809
63949
  }
63810
63950
  });
63811
63951
 
63812
- // ../system/dist/retired-settings-keys-Cxgwa0bk.js
63813
- var require_retired_settings_keys_Cxgwa0bk = __commonJS({
63814
- "../system/dist/retired-settings-keys-Cxgwa0bk.js"(exports) {
63952
+ // ../system/dist/retired-settings-keys-D61PxN8L.js
63953
+ var require_retired_settings_keys_D61PxN8L = __commonJS({
63954
+ "../system/dist/retired-settings-keys-D61PxN8L.js"(exports) {
63815
63955
  "use strict";
63816
- var require_dist10 = require_dist_B84YlKAH();
63956
+ var require_dist10 = require_dist_DUSfUAgz();
63817
63957
  function settingsStoreIsAuthoritativeHere(env) {
63818
63958
  const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
63819
63959
  return raw === "" || raw === "hub";
@@ -64093,9 +64233,9 @@ var require_retired_settings_keys_Cxgwa0bk = __commonJS({
64093
64233
  }
64094
64234
  });
64095
64235
 
64096
- // ../types/dist/canonical-hash-DNV8S5ET.js
64097
- var require_canonical_hash_DNV8S5ET = __commonJS({
64098
- "../types/dist/canonical-hash-DNV8S5ET.js"(exports) {
64236
+ // ../types/dist/canonical-hash-9lydiEzx.js
64237
+ var require_canonical_hash_9lydiEzx = __commonJS({
64238
+ "../types/dist/canonical-hash-9lydiEzx.js"(exports) {
64099
64239
  "use strict";
64100
64240
  var node_crypto = __require("crypto");
64101
64241
  var AUDIO_ENCODER_BY_CODEC = {
@@ -64182,6 +64322,7 @@ var require_canonical_hash_DNV8S5ET = __commonJS({
64182
64322
  ];
64183
64323
  if (video.preset !== void 0) args.push("-preset", video.preset);
64184
64324
  if (video.tune !== void 0) args.push("-tune", video.tune);
64325
+ if (video.singleSlicePerFrame === true && video.encoder === "libx264") args.push("-x264-params", "sliced-threads=0");
64185
64326
  if (video.profile !== void 0) args.push("-profile:v", video.profile);
64186
64327
  if (video.level !== void 0) args.push("-level", video.level);
64187
64328
  if (video.pixelFormat !== void 0) args.push("-pix_fmt", video.pixelFormat);
@@ -64365,6 +64506,7 @@ var require_canonical_hash_DNV8S5ET = __commonJS({
64365
64506
  scale,
64366
64507
  ...v.preset !== void 0 ? { preset: v.preset } : {},
64367
64508
  ...v.tune !== void 0 ? { tune: v.tune } : {},
64509
+ ...v.singleSlicePerFrame !== void 0 ? { singleSlicePerFrame: v.singleSlicePerFrame } : {},
64368
64510
  ...v.profile !== void 0 ? { profile: v.profile } : {},
64369
64511
  ...v.level !== void 0 ? { level: v.level } : {},
64370
64512
  ...input.pixelFormat !== void 0 ? { pixelFormat: input.pixelFormat } : {},
@@ -64695,7 +64837,7 @@ var require_node = __commonJS({
64695
64837
  value: mod,
64696
64838
  enumerable: true
64697
64839
  }) : target, mod));
64698
- var require_canonical_hash = require_canonical_hash_DNV8S5ET();
64840
+ var require_canonical_hash = require_canonical_hash_9lydiEzx();
64699
64841
  var require_err_msg = require_err_msg_COpsHMw2();
64700
64842
  var node_crypto = __require("crypto");
64701
64843
  var node_fs = __require("fs");
@@ -66048,8 +66190,8 @@ var require_device_manager_addon = __commonJS({
66048
66190
  [Symbol.toStringTag]: { value: "Module" }
66049
66191
  });
66050
66192
  require_chunk_Cek0wNdY();
66051
- var require_dist10 = require_dist_B84YlKAH();
66052
- var require_retired_settings_keys = require_retired_settings_keys_Cxgwa0bk();
66193
+ var require_dist10 = require_dist_DUSfUAgz();
66194
+ var require_retired_settings_keys = require_retired_settings_keys_D61PxN8L();
66053
66195
  var node_crypto = __require("crypto");
66054
66196
  var _camstack_types_node = require_node();
66055
66197
  var JOB_HISTORY = 20;
@@ -71674,7 +71816,7 @@ var require_hub_forwarder = __commonJS({
71674
71816
  [Symbol.toStringTag]: { value: "Module" }
71675
71817
  });
71676
71818
  require_chunk_Cek0wNdY();
71677
- var require_dist10 = require_dist_B84YlKAH();
71819
+ var require_dist10 = require_dist_DUSfUAgz();
71678
71820
  var require_formatter = require_formatter_DqAKDlvN();
71679
71821
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
71680
71822
  var HubForwarderDestination = class {
@@ -71811,7 +71953,7 @@ var require_liveness_monitor_addon = __commonJS({
71811
71953
  "use strict";
71812
71954
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
71813
71955
  require_chunk_Cek0wNdY();
71814
- var require_dist10 = require_dist_B84YlKAH();
71956
+ var require_dist10 = require_dist_DUSfUAgz();
71815
71957
  var NO_DEVICES = "liveness:no-devices";
71816
71958
  var ALL_OFFLINE = "liveness:all-devices-offline";
71817
71959
  var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
@@ -72007,7 +72149,7 @@ var require_local_auth_addon = __commonJS({
72007
72149
  [Symbol.toStringTag]: { value: "Module" }
72008
72150
  });
72009
72151
  var require_chunk = require_chunk_Cek0wNdY();
72010
- var require_dist10 = require_dist_B84YlKAH();
72152
+ var require_dist10 = require_dist_DUSfUAgz();
72011
72153
  var node_crypto = __require("crypto");
72012
72154
  node_crypto = require_chunk.__toESM(node_crypto);
72013
72155
  var crypto$1 = __require("crypto");
@@ -79820,7 +79962,7 @@ var require_loki_logging = __commonJS({
79820
79962
  [Symbol.toStringTag]: { value: "Module" }
79821
79963
  });
79822
79964
  require_chunk_Cek0wNdY();
79823
- var require_dist10 = require_dist_B84YlKAH();
79965
+ var require_dist10 = require_dist_DUSfUAgz();
79824
79966
  function sanitizeLabelName(raw) {
79825
79967
  const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
79826
79968
  return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
@@ -80407,7 +80549,7 @@ var require_native_metrics_addon = __commonJS({
80407
80549
  [Symbol.toStringTag]: { value: "Module" }
80408
80550
  });
80409
80551
  var require_chunk = require_chunk_Cek0wNdY();
80410
- var require_dist10 = require_dist_B84YlKAH();
80552
+ var require_dist10 = require_dist_DUSfUAgz();
80411
80553
  var node_fs_promises = __require("fs/promises");
80412
80554
  var node_fs = __require("fs");
80413
80555
  node_fs = require_chunk.__toESM(node_fs);
@@ -83160,7 +83302,7 @@ var require_filesystem_storage_addon = __commonJS({
83160
83302
  [Symbol.toStringTag]: { value: "Module" }
83161
83303
  });
83162
83304
  var require_chunk = require_chunk_Cek0wNdY();
83163
- var require_dist10 = require_dist_B84YlKAH();
83305
+ var require_dist10 = require_dist_DUSfUAgz();
83164
83306
  var node_crypto = __require("crypto");
83165
83307
  var node_fs_promises = __require("fs/promises");
83166
83308
  var node_path = __require("path");
@@ -84387,8 +84529,8 @@ var require_sqlite_settings_addon = __commonJS({
84387
84529
  [Symbol.toStringTag]: { value: "Module" }
84388
84530
  });
84389
84531
  var require_chunk = require_chunk_Cek0wNdY();
84390
- var require_dist10 = require_dist_B84YlKAH();
84391
- var require_retired_settings_keys = require_retired_settings_keys_Cxgwa0bk();
84532
+ var require_dist10 = require_dist_DUSfUAgz();
84533
+ var require_retired_settings_keys = require_retired_settings_keys_D61PxN8L();
84392
84534
  var require_wal_checkpoint_policy = require_wal_checkpoint_policy_CCJZngds();
84393
84535
  var node_crypto = __require("crypto");
84394
84536
  var node_path = __require("path");
@@ -87175,7 +87317,7 @@ var require_storage_orchestrator_addon = __commonJS({
87175
87317
  [Symbol.toStringTag]: { value: "Module" }
87176
87318
  });
87177
87319
  var require_chunk = require_chunk_Cek0wNdY();
87178
- var require_dist10 = require_dist_B84YlKAH();
87320
+ var require_dist10 = require_dist_DUSfUAgz();
87179
87321
  var require_hub_cap_forward = require_hub_cap_forward_DmHNjbB0();
87180
87322
  var zod = require_zod();
87181
87323
  var node_crypto = __require("crypto");
@@ -88520,6 +88662,57 @@ var require_storage_orchestrator_addon = __commonJS({
88520
88662
  const percent = require_dist10.gbToBytes(minFreeGb) / totalBytes * 100;
88521
88663
  return Math.min(100, percent);
88522
88664
  }
88665
+ function unknown(reason) {
88666
+ return {
88667
+ kind: "unknown",
88668
+ reason
88669
+ };
88670
+ }
88671
+ function freePercentOf(capacity) {
88672
+ if (capacity === null || capacity.totalBytes <= 0) return 100;
88673
+ return capacity.availableBytes / capacity.totalBytes * 100;
88674
+ }
88675
+ function volumeTotalBytesOf(capacity) {
88676
+ return capacity === null ? 0 : capacity.totalBytes;
88677
+ }
88678
+ function probePathFor(location, locality, localNodeId, cwd) {
88679
+ if (locality !== true) {
88680
+ const why = locality === false ? `is served by the remote provider "${location.providerId}" (nodeLocal: false) \u2014 its bytes live on the remote host and no local statfs describes them` : `is served by "${location.providerId}", which this node has not classified yet (no getProviderInfo answer) \u2014 a local statfs would be a guess`;
88681
+ return { reason: `location "${location.id}" ${why}` };
88682
+ }
88683
+ if (location.nodeId !== void 0 && location.nodeId !== localNodeId) return { reason: `location "${location.id}" lives on node "${location.nodeId}" and this is "${localNodeId}" \u2014 only the owning node can statfs its own volumes` };
88684
+ const basePath = location.config["basePath"];
88685
+ if (typeof basePath !== "string" || basePath.length === 0) return { reason: `location "${location.id}" declares no basePath to measure` };
88686
+ if (!node_path.default.isAbsolute(basePath)) return { reason: `location "${location.id}" declares a RELATIVE basePath ("${basePath}") \u2014 that is a path inside its provider's own namespace, not on this node's filesystem, and resolving it against "${cwd}" would measure an unrelated volume` };
88687
+ return { path: node_path.default.resolve(basePath) };
88688
+ }
88689
+ async function readLocalVolumeCapacity(location, locality, localNodeId, deps) {
88690
+ const target = probePathFor(location, locality, localNodeId, deps.cwd);
88691
+ if ("reason" in target) return unknown(target.reason);
88692
+ const root = node_path.default.parse(target.path).root;
88693
+ let probed = target.path;
88694
+ for (; ; ) try {
88695
+ await deps.probe.access(probed);
88696
+ break;
88697
+ } catch {
88698
+ const parent = node_path.default.dirname(probed);
88699
+ if (parent === probed) return unknown(`nothing on the path "${target.path}" exists on this node \u2014 the volume is not mounted, so the root filesystem's size is not an answer about it`);
88700
+ probed = parent;
88701
+ }
88702
+ if (probed === root && target.path !== root) return unknown(`only the filesystem root exists on the path "${target.path}" \u2014 the volume is not mounted, so the root filesystem's size is not an answer about it`);
88703
+ try {
88704
+ const st = await deps.probe.statfs(probed);
88705
+ return {
88706
+ kind: "measured",
88707
+ path: probed,
88708
+ totalBytes: st.blocks * st.bsize,
88709
+ availableBytes: st.bavail * st.bsize
88710
+ };
88711
+ } catch (err) {
88712
+ const reason = err instanceof Error ? err.message : String(err);
88713
+ return unknown(`statfs("${probed}") failed: ${reason}`);
88714
+ }
88715
+ }
88523
88716
  var FRAMEWORK_CONFIG_KEYS = /* @__PURE__ */ new Set([
88524
88717
  "basePath",
88525
88718
  "readOnly",
@@ -90572,31 +90765,53 @@ var require_storage_orchestrator_addon = __commonJS({
90572
90765
  } });
90573
90766
  throw new Error(refusal);
90574
90767
  }
90575
- /** statfs capacity of a location's basePath, walking up to the nearest
90576
- * existing ancestor. Null for remote-node locations or unstattable paths. */
90768
+ /**
90769
+ * The volume figures for one location, or `null` when this node cannot take
90770
+ * a measurement that is genuinely about it.
90771
+ *
90772
+ * The decision lives in `location-volume-capacity.ts` — including why a
90773
+ * remote-provider location (the SMB share that was reporting the hub's own
90774
+ * disk) gets nothing rather than a number. Here we only supply the node id,
90775
+ * the provider's declared locality and the real filesystem, and log the
90776
+ * reason once per distinct fault: `listLocations` is on the admin UI's poll,
90777
+ * so a line per call would be a line per second.
90778
+ */
90577
90779
  async localCapacityOf(loc) {
90578
- const localNode = this.service?.getLocalNodeId() ?? "hub";
90579
- if (loc.nodeId !== void 0 && loc.nodeId !== localNode) return null;
90580
- const bp = loc.config["basePath"];
90581
- if (typeof bp !== "string" || bp.length === 0) return null;
90582
- try {
90583
- let target = node_path.resolve(bp);
90584
- for (; ; ) try {
90585
- await node_fs_promises.access(target);
90586
- break;
90587
- } catch {
90588
- const parent = node_path.dirname(target);
90589
- if (!parent || parent === target) return null;
90590
- target = parent;
90591
- }
90592
- const st = await node_fs_promises.statfs(target);
90593
- return {
90594
- totalBytes: st.blocks * st.bsize,
90595
- availableBytes: st.bavail * st.bsize
90596
- };
90597
- } catch {
90780
+ const localNode = this.service?.getLocalNodeId() ?? HUB_NODE_ID;
90781
+ const result = await readLocalVolumeCapacity(loc, this.providerIsNodeLocal(loc), localNode, {
90782
+ probe: {
90783
+ access: (target) => node_fs_promises.access(target),
90784
+ statfs: async (target) => node_fs_promises.statfs(target)
90785
+ },
90786
+ cwd: node_path.resolve(".")
90787
+ });
90788
+ if (result.kind === "unknown") {
90789
+ this.logCapacityUnknownOnce(loc.id, result.reason);
90598
90790
  return null;
90599
90791
  }
90792
+ this.clearCapacityUnknown(loc.id, result.path);
90793
+ return {
90794
+ totalBytes: result.totalBytes,
90795
+ availableBytes: result.availableBytes
90796
+ };
90797
+ }
90798
+ /** locationId → the last unmeasurable reason logged for it. */
90799
+ capacityUnknownLogged = /* @__PURE__ */ new Map();
90800
+ logCapacityUnknownOnce(locationId, reason) {
90801
+ if (this.capacityUnknownLogged.get(locationId) === reason) return;
90802
+ this.capacityUnknownLogged.set(locationId, reason);
90803
+ this.ctx.logger.warn("storage-orchestrator: capacity is UNKNOWN for a location \u2014 reporting nothing rather than another volume\u2019s figures", { meta: {
90804
+ locationId,
90805
+ reason
90806
+ } });
90807
+ }
90808
+ clearCapacityUnknown(locationId, measuredPath) {
90809
+ if (!this.capacityUnknownLogged.has(locationId)) return;
90810
+ this.capacityUnknownLogged.delete(locationId);
90811
+ this.ctx.logger.info("storage-orchestrator: capacity is measurable again", { meta: {
90812
+ locationId,
90813
+ measuredPath
90814
+ } });
90600
90815
  }
90601
90816
  /** Per-location cap on EVICTABLE bytes (`config.maxUsedGb`): the operator's
90602
90817
  * "recordings may use at most N GB in total" — enforced by the pressure
@@ -90685,28 +90900,30 @@ var require_storage_orchestrator_addon = __commonJS({
90685
90900
  } });
90686
90901
  return occupancy;
90687
90902
  }
90688
- /** Free capacity (%) on a location's volume via `statfs`; 100 (guard inert) when unstattable. */
90903
+ /**
90904
+ * The pressure guard's two readings come from the SAME probe the UI's
90905
+ * capacity does — one capacity truth per location, not two.
90906
+ *
90907
+ * They used to `statfs` the raw `basePath` themselves, which on this node
90908
+ * answers for whatever local path happens to carry that name: a location
90909
+ * owned by another node, or served by a remote provider, could decide
90910
+ * eviction from the hub's own disk. Both now degrade exactly as they did
90911
+ * when the path was unstattable — `100` (guard inert) and `0` (no byte
90912
+ * target) — but they do so for every reading that is not ABOUT the location,
90913
+ * not only for the ones that threw.
90914
+ */
90915
+ async volumeCapacityOf(locationId) {
90916
+ const loc = this.service?.listLocations().find((l) => l.id === locationId);
90917
+ if (!loc) return null;
90918
+ return this.localCapacityOf(loc);
90919
+ }
90920
+ /** Free capacity (%) on a location's volume; 100 (guard inert) when unmeasurable. */
90689
90921
  async locationFreePercent(locationId) {
90690
- const bp = this.locationBasePath(locationId);
90691
- if (!bp) return 100;
90692
- try {
90693
- const st = await node_fs_promises.statfs(bp);
90694
- if (st.blocks <= 0) return 100;
90695
- return st.bavail / st.blocks * 100;
90696
- } catch {
90697
- return 100;
90698
- }
90922
+ return freePercentOf(await this.volumeCapacityOf(locationId));
90699
90923
  }
90700
- /** Total bytes on a location's volume via `statfs`; 0 when unstattable. */
90924
+ /** Total bytes on a location's volume; 0 when unmeasurable. */
90701
90925
  async locationVolumeTotalBytes(locationId) {
90702
- const bp = this.locationBasePath(locationId);
90703
- if (!bp) return 0;
90704
- try {
90705
- const st = await node_fs_promises.statfs(bp);
90706
- return st.blocks * st.bsize;
90707
- } catch {
90708
- return 0;
90709
- }
90926
+ return volumeTotalBytesOf(await this.volumeCapacityOf(locationId));
90710
90927
  }
90711
90928
  /**
90712
90929
  * Seed default storage locations from the addon-declared
@@ -90827,7 +91044,7 @@ var require_system_config_addon = __commonJS({
90827
91044
  [Symbol.toStringTag]: { value: "Module" }
90828
91045
  });
90829
91046
  require_chunk_Cek0wNdY();
90830
- var require_dist10 = require_dist_B84YlKAH();
91047
+ var require_dist10 = require_dist_DUSfUAgz();
90831
91048
  var SECTION_TITLES = {
90832
91049
  server: "Server",
90833
91050
  auth: "Authentication"
@@ -108888,7 +109105,7 @@ var require_winston_logging = __commonJS({
108888
109105
  [Symbol.toStringTag]: { value: "Module" }
108889
109106
  });
108890
109107
  var require_chunk = require_chunk_Cek0wNdY();
108891
- var require_dist10 = require_dist_B84YlKAH();
109108
+ var require_dist10 = require_dist_DUSfUAgz();
108892
109109
  var require_formatter = require_formatter_DqAKDlvN();
108893
109110
  var node_path = __require("path");
108894
109111
  node_path = require_chunk.__toESM(node_path);
@@ -110925,9 +111142,9 @@ var require_event_category_yW4s1MGD = __commonJS({
110925
111142
  }
110926
111143
  });
110927
111144
 
110928
- // ../types/dist/sleep-CfDqEZrs.js
110929
- var require_sleep_CfDqEZrs = __commonJS({
110930
- "../types/dist/sleep-CfDqEZrs.js"(exports) {
111145
+ // ../types/dist/sleep-DXsYD0RX.js
111146
+ var require_sleep_DXsYD0RX = __commonJS({
111147
+ "../types/dist/sleep-DXsYD0RX.js"(exports) {
110931
111148
  "use strict";
110932
111149
  var require_event_category = require_event_category_yW4s1MGD();
110933
111150
  var zod = require_zod();
@@ -113706,6 +113923,7 @@ var require_sleep_CfDqEZrs = __commonJS({
113706
113923
  listTracks: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listTracks", "query", input),
113707
113924
  listRecentTracks: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listRecentTracks", "query", input),
113708
113925
  listSummaries: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listSummaries", "query", input),
113926
+ listRecentSummaries: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listRecentSummaries", "query", input),
113709
113927
  getSummary: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getSummary", "query", input),
113710
113928
  clearTracks: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "clearTracks", "mutation", input),
113711
113929
  getMotionEvents: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getMotionEvents", "query", input),
@@ -114759,7 +114977,7 @@ var require_addon = __commonJS({
114759
114977
  "../types/dist/addon.js"(exports) {
114760
114978
  "use strict";
114761
114979
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
114762
- var require_sleep = require_sleep_CfDqEZrs();
114980
+ var require_sleep = require_sleep_DXsYD0RX();
114763
114981
  var require_event_category = require_event_category_yW4s1MGD();
114764
114982
  var require_err_msg = require_err_msg_COpsHMw2();
114765
114983
  var CAP_INPUT_DEFAULTS = Object.freeze({
@@ -114880,6 +115098,7 @@ var require_addon = __commonJS({
114880
115098
  "getMotionEvents": { "limit": 1e3 },
114881
115099
  "getObjectEvents": { "limit": 1e3 },
114882
115100
  "getSensorEvents": { "limit": 1e3 },
115101
+ "listRecentSummaries": { "limit": 100 },
114883
115102
  "listRecentTracks": { "limit": 200 },
114884
115103
  "listSummaries": { "limit": 50 },
114885
115104
  "reclaimDebugMedia": { "mode": "report" },
@@ -121624,9 +121843,9 @@ var require_dist2 = __commonJS({
121624
121843
  }
121625
121844
  });
121626
121845
 
121627
- // ../system/dist/manifest-system-deps-8x1jgPz_.js
121628
- var require_manifest_system_deps_8x1jgPz = __commonJS({
121629
- "../system/dist/manifest-system-deps-8x1jgPz_.js"(exports) {
121846
+ // ../system/dist/manifest-system-deps-CrSP0Kkp.js
121847
+ var require_manifest_system_deps_CrSP0Kkp = __commonJS({
121848
+ "../system/dist/manifest-system-deps-CrSP0Kkp.js"(exports) {
121630
121849
  "use strict";
121631
121850
  var require_chunk = require_chunk_Cek0wNdY();
121632
121851
  require_hub_cap_forward_DmHNjbB0();
@@ -123070,6 +123289,19 @@ var require_manifest_system_deps_8x1jgPz = __commonJS({
123070
123289
  });
123071
123290
  return cachedInvocation;
123072
123291
  }
123292
+ function buildNpmChildEnv(env, execPath) {
123293
+ const nodeDir = node_path.dirname(execPath);
123294
+ const current = env["PATH"];
123295
+ if (current === void 0 || current.length === 0) return {
123296
+ ...env,
123297
+ PATH: nodeDir
123298
+ };
123299
+ if (current === nodeDir || current.startsWith(`${nodeDir}${node_path.delimiter}`)) return { ...env };
123300
+ return {
123301
+ ...env,
123302
+ PATH: `${nodeDir}${node_path.delimiter}${current}`
123303
+ };
123304
+ }
123073
123305
  async function runNpm(args, options) {
123074
123306
  const invocation = await resolveNpmInvocation({
123075
123307
  cacheDir: options.cacheDir,
@@ -123078,7 +123310,8 @@ var require_manifest_system_deps_8x1jgPz = __commonJS({
123078
123310
  });
123079
123311
  return execFileAsync$2(invocation.command, [...invocation.argsPrefix, ...args], {
123080
123312
  ...options.cwd !== void 0 ? { cwd: options.cwd } : {},
123081
- timeout: options.timeout ?? 3e5
123313
+ timeout: options.timeout ?? 3e5,
123314
+ env: buildNpmChildEnv(options.env ?? process.env, process.execPath)
123082
123315
  });
123083
123316
  }
123084
123317
  async function resolveUncached(options) {
@@ -130581,6 +130814,12 @@ var require_manifest_system_deps_8x1jgPz = __commonJS({
130581
130814
  return buildNativeCapProxy;
130582
130815
  }
130583
130816
  });
130817
+ Object.defineProperty(exports, "buildNpmChildEnv", {
130818
+ enumerable: true,
130819
+ get: function() {
130820
+ return buildNpmChildEnv;
130821
+ }
130822
+ });
130584
130823
  Object.defineProperty(exports, "buildUdsNativeCapProxy", {
130585
130824
  enumerable: true,
130586
130825
  get: function() {
@@ -131148,11 +131387,11 @@ var require_manifest_system_deps_8x1jgPz = __commonJS({
131148
131387
  }
131149
131388
  });
131150
131389
 
131151
- // ../system/dist/custom-action-registry-DwaSEiji.js
131152
- var require_custom_action_registry_DwaSEiji = __commonJS({
131153
- "../system/dist/custom-action-registry-DwaSEiji.js"(exports) {
131390
+ // ../system/dist/custom-action-registry-DAFhWd0h.js
131391
+ var require_custom_action_registry_DAFhWd0h = __commonJS({
131392
+ "../system/dist/custom-action-registry-DAFhWd0h.js"(exports) {
131154
131393
  "use strict";
131155
- var require_dist10 = require_dist_B84YlKAH();
131394
+ var require_dist10 = require_dist_DUSfUAgz();
131156
131395
  var CustomActionRegistry = class {
131157
131396
  byAddon = /* @__PURE__ */ new Map();
131158
131397
  registerAddon(addonId, catalog, handler) {
@@ -134966,7 +135205,7 @@ var require_dist3 = __commonJS({
134966
135205
  "use strict";
134967
135206
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
134968
135207
  var require_chunk = require_chunk_Cek0wNdY();
134969
- var require_dist10 = require_dist_B84YlKAH();
135208
+ var require_dist10 = require_dist_DUSfUAgz();
134970
135209
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
134971
135210
  require_alerts();
134972
135211
  var require_formatter = require_formatter_DqAKDlvN();
@@ -134993,9 +135232,9 @@ var require_dist3 = __commonJS({
134993
135232
  var require_builtins_winston_logging_index = require_winston_logging();
134994
135233
  var require_event_loop_stall_monitor = require_event_loop_stall_monitor_CAsHdMwP();
134995
135234
  var require_tls$1 = require_tls_BFs4PzoW();
134996
- var require_manifest_system_deps = require_manifest_system_deps_8x1jgPz();
135235
+ var require_manifest_system_deps = require_manifest_system_deps_CrSP0Kkp();
134997
135236
  var require_resource_monitor = require_resource_monitor_LVE1BoLs();
134998
- var require_custom_action_registry = require_custom_action_registry_DwaSEiji();
135237
+ var require_custom_action_registry = require_custom_action_registry_DAFhWd0h();
134999
135238
  var zod = require_zod();
135000
135239
  var node_crypto = __require("crypto");
135001
135240
  var _camstack_types_node = require_node();
@@ -215841,6 +216080,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
215841
216080
  exports.buildLinkChain = require_manifest_system_deps.buildLinkChain;
215842
216081
  exports.buildNativeCapProxy = require_manifest_system_deps.buildNativeCapProxy;
215843
216082
  exports.buildNodeManifest = buildNodeManifest;
216083
+ exports.buildNpmChildEnv = require_manifest_system_deps.buildNpmChildEnv;
215844
216084
  exports.buildStorageLocationRegistry = require_builtins_storage_orchestrator_storage_orchestrator_addon.buildStorageLocationRegistry;
215845
216085
  exports.buildUdsNativeCapProxy = require_manifest_system_deps.buildUdsNativeCapProxy;
215846
216086
  exports.builderMountedCapNames = builderMountedCapNames;
@@ -216103,9 +216343,9 @@ var require_dist4 = __commonJS({
216103
216343
  "../types/dist/index.js"(exports) {
216104
216344
  "use strict";
216105
216345
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
216106
- var require_sleep = require_sleep_CfDqEZrs();
216346
+ var require_sleep = require_sleep_DXsYD0RX();
216107
216347
  var require_event_category = require_event_category_yW4s1MGD();
216108
- var require_canonical_hash = require_canonical_hash_DNV8S5ET();
216348
+ var require_canonical_hash = require_canonical_hash_9lydiEzx();
216109
216349
  var require_enums2 = require_enums();
216110
216350
  var require_err_msg = require_err_msg_COpsHMw2();
216111
216351
  var zod = require_zod();
@@ -216285,7 +216525,23 @@ var require_dist4 = __commonJS({
216285
216525
  "zerolatency",
216286
216526
  "film",
216287
216527
  "animation"
216288
- ]).optional()
216528
+ ]).optional(),
216529
+ /**
216530
+ * ONE slice per access unit.
216531
+ *
216532
+ * `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
216533
+ * the encoder as five NAL slices that share one RTP timestamp and carry one
216534
+ * marker bit. A libwebrtc depacketiser sees five frame-starts and one
216535
+ * frame-end per frame: the first pictures render and the video then freezes
216536
+ * for good while the audio, on its own plane, plays on. Measured on this hub
216537
+ * 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
216538
+ *
216539
+ * A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
216540
+ * `outputArgs` on purpose: an opaque array is part of the sharing key, so two
216541
+ * consumers meaning the same thing spelled differently would stop sharing one
216542
+ * child. Absent means "whatever the encoder does" — today's behaviour.
216543
+ */
216544
+ singleSlicePerFrame: zod.z.boolean().optional()
216289
216545
  });
216290
216546
  var AudioEncodeSchema = zod.z.union([zod.z.literal("passthrough"), zod.z.object({
216291
216547
  codec: zod.z.enum([
@@ -216358,7 +216614,11 @@ var require_dist4 = __commonJS({
216358
216614
  };
216359
216615
  var ALEXA_EGRESS_PROFILE = {
216360
216616
  ...BASE_LIVE_EGRESS_PROFILE,
216361
- audio: { codec: "opus" }
216617
+ audio: { codec: "opus" },
216618
+ video: {
216619
+ ...BASE_LIVE_EGRESS_PROFILE.video,
216620
+ singleSlicePerFrame: true
216621
+ }
216362
216622
  };
216363
216623
  var RATE_CONTROL_TIGHT = {
216364
216624
  kind: "cbr",
@@ -216446,7 +216706,8 @@ var require_dist4 = __commonJS({
216446
216706
  gopFrames: video.gopFrames ?? -1,
216447
216707
  bf: video.bf ?? -1,
216448
216708
  preset: video.preset ?? UNSET,
216449
- tune: video.tune ?? UNSET
216709
+ tune: video.tune ?? UNSET,
216710
+ singleSlicePerFrame: video.singleSlicePerFrame === void 0 ? UNSET : String(video.singleSlicePerFrame)
216450
216711
  };
216451
216712
  }
216452
216713
  function canonicalAudio(audio) {
@@ -231303,13 +231564,91 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
231303
231564
  deviceIds: zod.z.array(zod.z.number()).min(1),
231304
231565
  since: zod.z.number().optional(),
231305
231566
  until: zod.z.number().optional(),
231306
- limit: zod.z.number().int().min(1).max(200).default(50)
231567
+ limit: zod.z.number().int().min(1).max(200).default(50),
231568
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
231569
+ * meaning of "dog" on every summary surface. */
231570
+ classes: zod.z.array(zod.z.string().min(1).max(60)).max(40).optional()
231571
+ });
231572
+ var SummaryMemberSchema = zod.z.object({
231573
+ trackId: zod.z.string(),
231574
+ /**
231575
+ * The track's class at the time it JOINED, or `null` when it was unresolved
231576
+ * and the track no longer exists to fill it.
231577
+ *
231578
+ * The join, not the latest opinion: class voting can re-decide a track's
231579
+ * class afterwards, and a group's composition is the history of who came
231580
+ * together. A consumer counts these to badge a thumbnail — "two people and a
231581
+ * dog" — rather than being handed a second, denormalised tally that would
231582
+ * drift from the members it claims to describe.
231583
+ */
231584
+ className: zod.z.string().nullable(),
231585
+ /** `primary` is the one member that carries the group's identity. */
231586
+ role: zod.z.enum([
231587
+ "primary",
231588
+ "sibling",
231589
+ "child"
231590
+ ]).nullable(),
231591
+ joinedAt: zod.z.number(),
231592
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
231593
+ * at read time. Absent when nothing resolved, never an empty string. */
231594
+ label: zod.z.string().optional(),
231595
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
231596
+ * recognised nobody, and absent for a member whose track has been pruned —
231597
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
231598
+ subLabel: zod.z.string().optional()
231307
231599
  });
231308
- var SummariesPageSchema = zod.z.object({ summaries: zod.z.array(SummarySchema).readonly() });
231309
231600
  var SummaryDetailSchema = zod.z.object({
231310
231601
  summary: SummarySchema,
231311
- /** Member track ids, in absorption order. */
231312
- trackIds: zod.z.array(zod.z.string()).readonly()
231602
+ /** Members in absorption order, oldest join first. */
231603
+ members: zod.z.array(SummaryMemberSchema).readonly()
231604
+ });
231605
+ var SummaryListItemSchema = zod.z.object({
231606
+ summary: SummarySchema,
231607
+ /** Class counts, most numerous first; `className: null` is a member whose
231608
+ * class never resolved, counted so the counts add up to `memberCount`. */
231609
+ classes: zod.z.array(zod.z.object({
231610
+ className: zod.z.string().nullable(),
231611
+ count: zod.z.number().int().positive()
231612
+ })).readonly()
231613
+ });
231614
+ var SummariesPageSchema = zod.z.object({ summaries: zod.z.array(SummaryListItemSchema).readonly() });
231615
+ var RecentSummariesQueryInput = zod.z.object({
231616
+ /** Devices to merge. An empty array yields an empty page. */
231617
+ deviceIds: zod.z.array(zod.z.number()),
231618
+ /** Window lower bound on `lastActiveAt` (inclusive). */
231619
+ since: zod.z.number().optional(),
231620
+ /** Window upper bound on `lastActiveAt` (inclusive). */
231621
+ until: zod.z.number().optional(),
231622
+ limit: zod.z.number().int().min(1).max(500).default(100),
231623
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
231624
+ cursor: zod.z.string().optional(),
231625
+ /**
231626
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
231627
+ * deliberately the same name: the events filter tree emits one list of ticked
231628
+ * taxonomy nodes, and a summaries feed that called it something else would
231629
+ * make the operator's one filter component produce two shapes.
231630
+ *
231631
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
231632
+ * me the arrivals with a dog" has to return the group of four people who had
231633
+ * one with them; a filter on the group's own fields could only ever answer
231634
+ * about its primary.
231635
+ *
231636
+ * Matched against all THREE tiers of a member — the class (`animal`), the
231637
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
231638
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
231639
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
231640
+ * that read only the class would answer "animal" and never "dog".
231641
+ *
231642
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
231643
+ */
231644
+ classes: zod.z.array(zod.z.string().min(1).max(60)).max(40).optional()
231645
+ });
231646
+ var RecentSummariesPageSchema = zod.z.object({
231647
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
231648
+ * {@link listSummaries} — one list shape, so a card renders the same
231649
+ * whichever feed drew it. */
231650
+ summaries: zod.z.array(SummaryListItemSchema).readonly(),
231651
+ nextCursor: zod.z.string().nullable()
231313
231652
  });
231314
231653
  var RecentTracksPageSchema = zod.z.object({
231315
231654
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -231662,6 +232001,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
231662
232001
  listRecentTracks: require_sleep.method(RecentTracksQueryInput, RecentTracksPageSchema),
231663
232002
  /** Newest first by `openedAt`, for a camera set, inside an optional window. */
231664
232003
  listSummaries: require_sleep.method(SummariesQueryInput, SummariesPageSchema),
232004
+ /**
232005
+ * The PAGED feed — the reel and the Events gallery. Same cursor contract as
232006
+ * {@link listRecentTracks}, so one screen cannot hold two ideas of newest.
232007
+ */
232008
+ listRecentSummaries: require_sleep.method(RecentSummariesQueryInput, RecentSummariesPageSchema),
231665
232009
  /** One summary with its member track ids; `null` for an id nobody wrote. */
231666
232010
  getSummary: require_sleep.method(zod.z.object({ id: zod.z.string() }), SummaryDetailSchema.nullable()),
231667
232011
  clearTracks: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.void(), {
@@ -244265,7 +244609,29 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
244265
244609
  * disarmed the channels it did not mention would make the Levels page and
244266
244610
  * the Diagnostics page fight over the same value.
244267
244611
  */
244268
- channels: zod.z.array(LogChannelWindowPatchSchema).readonly().optional()
244612
+ channels: zod.z.array(LogChannelWindowPatchSchema).readonly().optional(),
244613
+ /**
244614
+ * How many days the analytics OPS LOG is kept — the audit trail of what
244615
+ * retention actually deleted.
244616
+ *
244617
+ * It lives HERE, on the logging document, and not on the analytics addon's
244618
+ * settings form, on the operator's instruction: every other log-ish window in
244619
+ * this system is configured through this one document, and a retention audit
244620
+ * configured somewhere else is a second place to look when the question is
244621
+ * "what happened to my data".
244622
+ *
244623
+ * That placement has a cost worth stating: this document ships in the
244624
+ * framework closure, so this knob needs a published `@camstack/server` while
244625
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
244626
+ *
244627
+ * `retention-model.ts` argued this one must NOT follow a device window,
244628
+ * because the audit trail is the history of the SWEEP and not of the camera.
244629
+ * That argument stands and is untouched — what changes is that 30 days stops
244630
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
244631
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
244632
+ * state in which the next 9.5 GiB leak cannot be explained.
244633
+ */
244634
+ opsLogRetentionDays: zod.z.number().int().min(0).max(3650).optional()
244269
244635
  });
244270
244636
  var ReportedLoadContributionSchema = LoadContributionSchema.extend({ addonId: zod.z.string() });
244271
244637
  var ReportedFailureContributionSchema = FailureContributionSchema.extend({ addonId: zod.z.string() });
@@ -244304,6 +244670,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
244304
244670
  channels: zod.z.array(LogChannelDescriptorSchema).readonly(),
244305
244671
  /** The channels ARMED right now, each with its deadline. */
244306
244672
  activeChannels: zod.z.array(LogChannelWindowStateSchema).readonly(),
244673
+ /**
244674
+ * The ops-log window in days, as it is in force. `0` = kept forever.
244675
+ *
244676
+ * Reported rather than left to the caller's memory of what it wrote: this is
244677
+ * the value the analytics sweep will actually use, and an operator asking how
244678
+ * long their audit trail lives should not have to infer it from a default.
244679
+ */
244680
+ opsLogRetentionDays: zod.z.number().int().min(0),
244307
244681
  persisted: zod.z.boolean()
244308
244682
  });
244309
244683
  var systemCapability = {
@@ -252782,6 +253156,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
252782
253156
  addonId: null,
252783
253157
  access: "view"
252784
253158
  },
253159
+ "pipelineAnalytics.listRecentSummaries": {
253160
+ capName: "pipeline-analytics",
253161
+ capScope: "device",
253162
+ addonId: null,
253163
+ access: "view"
253164
+ },
252785
253165
  "pipelineAnalytics.listRecentTracks": {
252786
253166
  capName: "pipeline-analytics",
252787
253167
  capScope: "device",
@@ -256873,6 +257253,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
256873
257253
  form: "single",
256874
257254
  optional: true
256875
257255
  }],
257256
+ "pipelineAnalytics.listRecentSummaries": [{
257257
+ name: "deviceIds",
257258
+ form: "array",
257259
+ optional: false
257260
+ }],
256876
257261
  "pipelineAnalytics.listRecentTracks": [{
256877
257262
  name: "deviceIds",
256878
257263
  form: "array",
@@ -264680,6 +265065,15 @@ var require_server_root = __commonJS({
264680
265065
  __name(buildNpmRegistryArgs, "buildNpmRegistryArgs");
264681
265066
  var NPM_VIEW_TIMEOUT_MS = 2e4;
264682
265067
  var NPM_INSTALL_TIMEOUT_MS = 15 * 6e4;
265068
+ var CLOSURE_ALLOW_SCRIPTS = {
265069
+ "@camstack/server": true,
265070
+ "better-sqlite3": true,
265071
+ "node-av": true,
265072
+ "node-pty": true,
265073
+ ssh2: true,
265074
+ "cpu-features": true,
265075
+ esbuild: true
265076
+ };
264683
265077
  var RESTART_REASON_PREFIX = "server-update";
264684
265078
  var STALE_TRANSIENT_MS = 24 * 60 * 60 * 1e3;
264685
265079
  function readWrapperProbes() {
@@ -264727,6 +265121,7 @@ var require_server_root = __commonJS({
264727
265121
  dataDir;
264728
265122
  execNpm;
264729
265123
  ensureNativePrebuildsFn;
265124
+ applyClosurePatchesFn;
264730
265125
  env;
264731
265126
  readWrapperProbesFn;
264732
265127
  now;
@@ -264755,6 +265150,7 @@ var require_server_root = __commonJS({
264755
265150
  };
264756
265151
  });
264757
265152
  this.ensureNativePrebuildsFn = options.ensureNativePrebuilds ?? (async () => void 0);
265153
+ this.applyClosurePatchesFn = options.applyClosurePatches ?? (async () => void 0);
264758
265154
  this.env = options.env ?? process.env;
264759
265155
  this.readWrapperProbesFn = options.readWrapperProbes ?? readWrapperProbes;
264760
265156
  this.now = options.now ?? Date.now;
@@ -265070,7 +265466,8 @@ var require_server_root = __commonJS({
265070
265466
  } else {
265071
265467
  fs6.writeFileSync(path6.join(stagingDir, "package.json"), JSON.stringify({
265072
265468
  name: "camstack-node-root",
265073
- private: true
265469
+ private: true,
265470
+ allowScripts: CLOSURE_ALLOW_SCRIPTS
265074
265471
  }, null, 2), "utf-8");
265075
265472
  await this.execNpm([
265076
265473
  ...installArgs,
@@ -265103,6 +265500,15 @@ var require_server_root = __commonJS({
265103
265500
  if (missingNatives.length > 0) {
265104
265501
  throw new Error(`staged closure missing native prebuilds: ${missingNatives.join(", ")} \u2014 refusing to arm a swap that would break the forked runners`);
265105
265502
  }
265503
+ try {
265504
+ await this.applyClosurePatchesFn(stagingDir);
265505
+ } catch (err) {
265506
+ this.logger.warn("closure-patch apply step failed \u2014 validator will decide", {
265507
+ meta: {
265508
+ error: err instanceof Error ? err.message : String(err)
265509
+ }
265510
+ });
265511
+ }
265106
265512
  const unpatched = findUnappliedClosurePatches(stagingDir);
265107
265513
  if (unpatched.length > 0) {
265108
265514
  throw new Error(`staged closure did not apply its shipped patches: ${unpatched.join(", ")} \u2014 refusing to arm a swap into an unpatched closure`);
@@ -265150,6 +265556,7 @@ var require_server_root = __commonJS({
265150
265556
  return {
265151
265557
  name: "camstack-node-root",
265152
265558
  private: true,
265559
+ allowScripts: CLOSURE_ALLOW_SCRIPTS,
265153
265560
  dependencies: {
265154
265561
  [this.spec.packageName]: fileRef(rootTgz)
265155
265562
  },
@@ -396107,6 +396514,78 @@ var require_system_ensure_prebuilds = __commonJS({
396107
396514
  }
396108
396515
  });
396109
396516
 
396517
+ // ../../server/backend/dist/core/server-update/system-apply-closure-patches.js
396518
+ var require_system_apply_closure_patches = __commonJS({
396519
+ "../../server/backend/dist/core/server-update/system-apply-closure-patches.js"(exports) {
396520
+ "use strict";
396521
+ var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
396522
+ if (k2 === void 0) k2 = k;
396523
+ var desc = Object.getOwnPropertyDescriptor(m, k);
396524
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
396525
+ desc = { enumerable: true, get: function() {
396526
+ return m[k];
396527
+ } };
396528
+ }
396529
+ Object.defineProperty(o, k2, desc);
396530
+ }) : (function(o, m, k, k2) {
396531
+ if (k2 === void 0) k2 = k;
396532
+ o[k2] = m[k];
396533
+ }));
396534
+ var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
396535
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
396536
+ }) : function(o, v) {
396537
+ o["default"] = v;
396538
+ });
396539
+ var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() {
396540
+ var ownKeys = function(o) {
396541
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
396542
+ var ar = [];
396543
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
396544
+ return ar;
396545
+ };
396546
+ return ownKeys(o);
396547
+ };
396548
+ return function(mod) {
396549
+ if (mod && mod.__esModule) return mod;
396550
+ var result = {};
396551
+ if (mod != null) {
396552
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
396553
+ }
396554
+ __setModuleDefault(result, mod);
396555
+ return result;
396556
+ };
396557
+ })();
396558
+ Object.defineProperty(exports, "__esModule", { value: true });
396559
+ exports.buildSystemApplyClosurePatches = buildSystemApplyClosurePatches;
396560
+ var node_child_process_1 = __require("child_process");
396561
+ var fs = __importStar(__require("fs"));
396562
+ var path = __importStar(__require("path"));
396563
+ var node_util_1 = __require("util");
396564
+ var execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
396565
+ var APPLIER_REL = ["node_modules", "@camstack", "server", "scripts", "closure-postinstall.mjs"];
396566
+ var APPLY_TIMEOUT_MS = 6e4;
396567
+ function buildSystemApplyClosurePatches(logger) {
396568
+ return async (closureDir) => {
396569
+ const applier = path.join(closureDir, ...APPLIER_REL);
396570
+ if (!fs.existsSync(applier)) {
396571
+ logger.debug("closure ships no patch applier \u2014 nothing to apply", {
396572
+ meta: { applier }
396573
+ });
396574
+ return;
396575
+ }
396576
+ const { stdout, stderr } = await execFileAsync(process.execPath, [applier], {
396577
+ cwd: path.dirname(path.dirname(applier)),
396578
+ timeout: APPLY_TIMEOUT_MS
396579
+ });
396580
+ const output = `${stdout}${stderr}`.trim();
396581
+ if (output.length > 0) {
396582
+ logger.info("closure patches applied", { meta: { output } });
396583
+ }
396584
+ };
396585
+ }
396586
+ }
396587
+ });
396588
+
396110
396589
  // ../../server/backend/dist/core/server-update/system-exec-npm.js
396111
396590
  var require_system_exec_npm = __commonJS({
396112
396591
  "../../server/backend/dist/core/server-update/system-exec-npm.js"(exports) {
@@ -396494,6 +396973,7 @@ var require_agent_update_service = __commonJS({
396494
396973
  var system_1 = require_dist3();
396495
396974
  var types_1 = require_dist4();
396496
396975
  var system_ensure_prebuilds_js_1 = require_system_ensure_prebuilds();
396976
+ var system_apply_closure_patches_js_1 = require_system_apply_closure_patches();
396497
396977
  var system_exec_npm_js_1 = require_system_exec_npm();
396498
396978
  var update_availability_emitter_js_1 = require_update_availability_emitter();
396499
396979
  var update_availability_store_js_1 = require_update_availability_store();
@@ -396582,6 +397062,11 @@ var require_agent_update_service = __commonJS({
396582
397062
  // ships the identical bare Node, so its `applyServerUpdate` staging would
396583
397063
  // hit the same source-only `better-sqlite3` miss without this.
396584
397064
  ensureNativePrebuilds: options.ensureNativePrebuilds ?? (0, system_ensure_prebuilds_js_1.buildSystemEnsureNativePrebuilds)(options.logger),
397065
+ // Apply the closure patches ourselves. npm 12 blocks the root package's
397066
+ // postinstall by default, and a silently-unpatched closure is refused by
397067
+ // the validator — an update that can never apply. Idempotent, so this
397068
+ // costs nothing where npm did run the lifecycle.
397069
+ applyClosurePatches: options.applyClosurePatches ?? (0, system_apply_closure_patches_js_1.buildSystemApplyClosurePatches)(options.logger),
396585
397070
  env: options.env,
396586
397071
  now: options.now
396587
397072
  });
@@ -421021,7 +421506,7 @@ var require_logging_settings = __commonJS({
421021
421506
  "../../server/backend/dist/api/core/logging-settings.js"(exports) {
421022
421507
  "use strict";
421023
421508
  Object.defineProperty(exports, "__esModule", { value: true });
421024
- exports.LoggingSettingsService = exports.MAX_CHANNEL_WINDOW_MS = exports.EMPTY_LOG_CHANNEL_PLANE = exports.HUB_NODE_ID = exports.LOGGING_SETTINGS_KEY = void 0;
421509
+ exports.LoggingSettingsService = exports.MAX_CHANNEL_WINDOW_MS = exports.EMPTY_LOG_CHANNEL_PLANE = exports.OPS_LOG_RETENTION_DEFAULT_DAYS = exports.HUB_NODE_ID = exports.LOGGING_SETTINGS_KEY = void 0;
421025
421510
  exports.buildLogChannelPlane = buildLogChannelPlane;
421026
421511
  exports.toLoggingSettingsRecord = toLoggingSettingsRecord;
421027
421512
  exports.resolveLevel = resolveLevel;
@@ -421037,6 +421522,7 @@ var require_logging_settings = __commonJS({
421037
421522
  var SYSTEM_SETTINGS_COLLECTION = "system-settings";
421038
421523
  exports.HUB_NODE_ID = "hub";
421039
421524
  var LOG_LEVELS = ["debug", "info", "warn", "error"];
421525
+ exports.OPS_LOG_RETENTION_DEFAULT_DAYS = 30;
421040
421526
  exports.EMPTY_LOG_CHANNEL_PLANE = {
421041
421527
  declarations: async () => [],
421042
421528
  apply: async () => {
@@ -421118,6 +421604,9 @@ var require_logging_settings = __commonJS({
421118
421604
  if (levelChanged) {
421119
421605
  next = mergeLevel(next, scope, component, patch.level ?? null, this.now());
421120
421606
  }
421607
+ if (patch.opsLogRetentionDays !== void 0) {
421608
+ next = { ...next, opsLogRetentionDays: patch.opsLogRetentionDays, updatedAt: this.now() };
421609
+ }
421121
421610
  if (channelsChanged) {
421122
421611
  const declared = await this.channels.declarations();
421123
421612
  const rejected = validateChannelPatch(patch.channels ?? [], declared);
@@ -421207,6 +421696,7 @@ var require_logging_settings = __commonJS({
421207
421696
  activeWindows: await this.describeWindows(),
421208
421697
  channels: await this.declaredChannels(),
421209
421698
  activeChannels: describeChannelWindows(record ?? {}, this.now()),
421699
+ opsLogRetentionDays: record?.opsLogRetentionDays ?? exports.OPS_LOG_RETENTION_DEFAULT_DAYS,
421210
421700
  persisted: record !== null
421211
421701
  };
421212
421702
  }
@@ -434655,6 +435145,7 @@ var require_server_update_service = __commonJS({
434655
435145
  var index_js_1 = require_server_root();
434656
435146
  var update_availability_emitter_js_1 = require_update_availability_emitter();
434657
435147
  var system_ensure_prebuilds_js_1 = require_system_ensure_prebuilds();
435148
+ var system_apply_closure_patches_js_1 = require_system_apply_closure_patches();
434658
435149
  var system_exec_npm_js_1 = require_system_exec_npm();
434659
435150
  var ServerUpdateService = class extends index_js_1.RootUpdateService {
434660
435151
  updateAvailability;
@@ -434688,6 +435179,11 @@ var require_server_update_service = __commonJS({
434688
435179
  // native-prebuild validator refuses the swap (toolchain-less packaged
434689
435180
  // desktop). No-op on docker where the install already carried it.
434690
435181
  ensureNativePrebuilds: options.ensureNativePrebuilds ?? (0, system_ensure_prebuilds_js_1.buildSystemEnsureNativePrebuilds)(options.logger),
435182
+ // Apply the closure patches ourselves. npm 12 blocks the root package's
435183
+ // postinstall by default, and a silently-unpatched closure is refused by
435184
+ // the validator — an update that can never apply. Idempotent, so this
435185
+ // costs nothing where npm did run the lifecycle.
435186
+ applyClosurePatches: options.applyClosurePatches ?? (0, system_apply_closure_patches_js_1.buildSystemApplyClosurePatches)(options.logger),
434691
435187
  env: options.env,
434692
435188
  now: options.now
434693
435189
  });