camstack 1.2.39 → 1.2.40

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.
@@ -23631,9 +23631,9 @@ var require_zod = __commonJS({
23631
23631
  }
23632
23632
  });
23633
23633
 
23634
- // ../system/dist/dist-C2_1HCpW.js
23635
- var require_dist_C2_1HCpW = __commonJS({
23636
- "../system/dist/dist-C2_1HCpW.js"(exports) {
23634
+ // ../system/dist/dist-DnhGRFEn.js
23635
+ var require_dist_DnhGRFEn = __commonJS({
23636
+ "../system/dist/dist-DnhGRFEn.js"(exports) {
23637
23637
  "use strict";
23638
23638
  var zod = require_zod();
23639
23639
  var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
@@ -24111,6 +24111,13 @@ var require_dist_C2_1HCpW = __commonJS({
24111
24111
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
24112
24112
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
24113
24113
  _registeredCapNames = [];
24114
+ /**
24115
+ * True only after `readAddonStore` actually answered. Constructor
24116
+ * defaults look like stored config when the store is down — a forked
24117
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
24118
+ * mode, 2026-08-25) is not "the operator chose this".
24119
+ */
24120
+ settingsStoreReady = false;
24114
24121
  /** Default config values. Provided via constructor. */
24115
24122
  defaults;
24116
24123
  constructor(defaults) {
@@ -24516,7 +24523,9 @@ var require_dist_C2_1HCpW = __commonJS({
24516
24523
  ];
24517
24524
  let lastErr;
24518
24525
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
24519
- return await settings.readAddonStore() ?? {};
24526
+ const stored = await settings.readAddonStore() ?? {};
24527
+ this.settingsStoreReady = true;
24528
+ return stored;
24520
24529
  } catch (err) {
24521
24530
  lastErr = err;
24522
24531
  const msg = err instanceof Error ? err.message : String(err);
@@ -24524,6 +24533,7 @@ var require_dist_C2_1HCpW = __commonJS({
24524
24533
  if (attempt === delaysMs.length) break;
24525
24534
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
24526
24535
  }
24536
+ this.settingsStoreReady = false;
24527
24537
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries \u2014 using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
24528
24538
  return {};
24529
24539
  }
@@ -26318,6 +26328,12 @@ var require_dist_C2_1HCpW = __commonJS({
26318
26328
  */
26319
26329
  resolution: zod.z.number().int().positive().optional()
26320
26330
  });
26331
+ var ModelProviderIdSchema = zod.z.enum([
26332
+ "camstack",
26333
+ "frigate",
26334
+ "scrypted",
26335
+ "custom"
26336
+ ]);
26321
26337
  var ModelCatalogEntrySchema = zod.z.object({
26322
26338
  id: zod.z.string(),
26323
26339
  name: zod.z.string(),
@@ -26415,6 +26431,12 @@ var require_dist_C2_1HCpW = __commonJS({
26415
26431
  */
26416
26432
  group: ModelVariantGroupSchema.optional(),
26417
26433
  /**
26434
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
26435
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
26436
+ * persisted before this field existed (`inferModelProvider` fills those).
26437
+ */
26438
+ provider: ModelProviderIdSchema.optional(),
26439
+ /**
26418
26440
  * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
26419
26441
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
26420
26442
  * labels already ARE the CamStack macros (Scrypted identity map).
@@ -33183,14 +33205,22 @@ var require_dist_C2_1HCpW = __commonJS({
33183
33205
  sustainSeconds: zod.z.number().int().min(0).max(3600).default(15)
33184
33206
  });
33185
33207
  var NcAudioConditionSchema = zod.z.object({
33186
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
33208
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
33187
33209
  labels: zod.z.array(zod.z.string().min(1)).min(1).optional(),
33188
33210
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
33189
33211
  dbThreshold: zod.z.number().min(-96).max(0).optional(),
33190
33212
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
33191
33213
  hitPercent: zod.z.number().int().min(1).max(100).default(60),
33192
33214
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
33193
- samplingSeconds: zod.z.number().int().min(1).max(300).default(10)
33215
+ samplingSeconds: zod.z.number().int().min(1).max(300).default(10),
33216
+ /**
33217
+ * LABEL MODE: how many labelled frames must land inside
33218
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
33219
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
33220
+ */
33221
+ confirmHits: zod.z.number().int().min(1).max(20).optional(),
33222
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
33223
+ confirmWindowSec: zod.z.number().int().min(1).max(60).optional()
33194
33224
  });
33195
33225
  var NcCrossingSchema = zod.z.enum([
33196
33226
  "enter",
@@ -35008,6 +35038,46 @@ var require_dist_C2_1HCpW = __commonJS({
35008
35038
  /** Cursor for the next page, or null when this page is the last. */
35009
35039
  nextCursor: zod.z.string().nullable()
35010
35040
  });
35041
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
35042
+ var LIST_GROUPS_MAX_LIMIT = 100;
35043
+ var AnalyticsGroupRecordSchema = zod.z.object({
35044
+ id: zod.z.string(),
35045
+ deviceId: zod.z.number().int(),
35046
+ openedAt: zod.z.number().int(),
35047
+ closedAt: zod.z.number().int(),
35048
+ timestamp: zod.z.number().int(),
35049
+ memberCount: zod.z.number().int(),
35050
+ memberTrackIds: zod.z.array(zod.z.string()).readonly(),
35051
+ className: zod.z.string(),
35052
+ classes: zod.z.array(zod.z.string()).readonly(),
35053
+ /** Relative event-media path, or null when the group has no picture yet. */
35054
+ mediaUrl: zod.z.string().nullable(),
35055
+ singleton: zod.z.boolean()
35056
+ });
35057
+ var AnalyticsGroupMemberSchema = zod.z.object({
35058
+ trackId: zod.z.string(),
35059
+ deviceId: zod.z.number().int(),
35060
+ className: zod.z.string(),
35061
+ firstSeen: zod.z.number().int(),
35062
+ lastSeen: zod.z.number().int(),
35063
+ mediaUrl: zod.z.string().nullable()
35064
+ });
35065
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: zod.z.array(AnalyticsGroupMemberSchema).readonly() });
35066
+ var ListGroupsQueryInput = zod.z.object({
35067
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
35068
+ deviceIds: zod.z.array(zod.z.number()),
35069
+ /** Window lower bound on `closedAt` (inclusive). */
35070
+ since: zod.z.number().optional(),
35071
+ /** Window upper bound on `openedAt` (inclusive). */
35072
+ until: zod.z.number().optional(),
35073
+ limit: zod.z.number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
35074
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
35075
+ cursor: zod.z.string().optional()
35076
+ });
35077
+ var ListGroupsPageSchema = zod.z.object({
35078
+ groups: zod.z.array(AnalyticsGroupRecordSchema).readonly(),
35079
+ nextCursor: zod.z.string().nullable()
35080
+ });
35011
35081
  var KeyEventQueryInput = zod.z.object({
35012
35082
  deviceId: zod.z.number(),
35013
35083
  /** Window lower bound (track firstSeen ≥ since). */
@@ -35083,7 +35153,9 @@ var require_dist_C2_1HCpW = __commonJS({
35083
35153
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
35084
35154
  plates: zod.z.number().int(),
35085
35155
  /** Per-track CLIP search vectors removed (best-effort). */
35086
- embeddings: zod.z.number().int()
35156
+ embeddings: zod.z.number().int(),
35157
+ /** Group membership + group rows removed with their last member (best-effort). */
35158
+ groups: zod.z.number().int()
35087
35159
  });
35088
35160
  var DiskReconcileCountsSchema = zod.z.object({
35089
35161
  mediaDropped: zod.z.number().int(),
@@ -35225,6 +35297,16 @@ var require_dist_C2_1HCpW = __commonJS({
35225
35297
  * are not included (same contract as `listTracks`).
35226
35298
  */
35227
35299
  listRecentTracks: method(RecentTracksQueryInput, RecentTracksPageSchema),
35300
+ /**
35301
+ * Batched co-moving group listing — the Groups feed. Same merge/cursor
35302
+ * contract as {@link listRecentTracks}. A group is a sealed partition of
35303
+ * one session; `getGroup` is the detail with members.
35304
+ */
35305
+ listGroups: method(ListGroupsQueryInput, ListGroupsPageSchema),
35306
+ getGroup: method(zod.z.object({
35307
+ deviceId: zod.z.number(),
35308
+ groupId: zod.z.string().min(1)
35309
+ }), AnalyticsGroupDetailSchema.nullable()),
35228
35310
  clearTracks: method(zod.z.object({ deviceId: zod.z.number() }), zod.z.void(), {
35229
35311
  kind: "mutation",
35230
35312
  auth: "admin"
@@ -35906,7 +35988,8 @@ var require_dist_C2_1HCpW = __commonJS({
35906
35988
  sizeMB: zod.z.number()
35907
35989
  })),
35908
35990
  group: ModelVariantGroupSchema.optional(),
35909
- legacy: zod.z.boolean().optional()
35991
+ legacy: zod.z.boolean().optional(),
35992
+ provider: ModelProviderIdSchema.optional()
35910
35993
  });
35911
35994
  var ConfigFieldBridge = zod.z.custom();
35912
35995
  var PipelineAddonSchemaSchema = zod.z.object({
@@ -44215,7 +44298,12 @@ var require_dist_C2_1HCpW = __commonJS({
44215
44298
  plateBbox: BoundingBoxSchema.optional(),
44216
44299
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
44217
44300
  keyFrameMediaKey: zod.z.string().optional(),
44218
- base64: zod.z.string().optional()
44301
+ base64: zod.z.string().optional(),
44302
+ /**
44303
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
44304
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
44305
+ */
44306
+ cropUrl: zod.z.string().optional()
44219
44307
  });
44220
44308
  var MediaFileLiteSchema = zod.z.object({
44221
44309
  key: zod.z.string(),
@@ -50934,6 +51022,12 @@ var require_dist_C2_1HCpW = __commonJS({
50934
51022
  addonId: null,
50935
51023
  access: "view"
50936
51024
  },
51025
+ "pipelineAnalytics.getGroup": {
51026
+ capName: "pipeline-analytics",
51027
+ capScope: "device",
51028
+ addonId: null,
51029
+ access: "view"
51030
+ },
50937
51031
  "pipelineAnalytics.getKeyEvents": {
50938
51032
  capName: "pipeline-analytics",
50939
51033
  capScope: "device",
@@ -51018,6 +51112,12 @@ var require_dist_C2_1HCpW = __commonJS({
51018
51112
  addonId: null,
51019
51113
  access: "view"
51020
51114
  },
51115
+ "pipelineAnalytics.listGroups": {
51116
+ capName: "pipeline-analytics",
51117
+ capScope: "device",
51118
+ addonId: null,
51119
+ access: "view"
51120
+ },
51021
51121
  "pipelineAnalytics.listOpsLog": {
51022
51122
  capName: "pipeline-analytics",
51023
51123
  capScope: "device",
@@ -54437,6 +54537,11 @@ var require_dist_C2_1HCpW = __commonJS({
54437
54537
  form: "single",
54438
54538
  optional: false
54439
54539
  }],
54540
+ "pipelineAnalytics.getGroup": [{
54541
+ name: "deviceId",
54542
+ form: "single",
54543
+ optional: false
54544
+ }],
54440
54545
  "pipelineAnalytics.getKeyEvents": [{
54441
54546
  name: "deviceId",
54442
54547
  form: "single",
@@ -54492,6 +54597,11 @@ var require_dist_C2_1HCpW = __commonJS({
54492
54597
  form: "array",
54493
54598
  optional: false
54494
54599
  }],
54600
+ "pipelineAnalytics.listGroups": [{
54601
+ name: "deviceIds",
54602
+ form: "array",
54603
+ optional: false
54604
+ }],
54495
54605
  "pipelineAnalytics.listOpsLog": [{
54496
54606
  name: "deviceId",
54497
54607
  form: "single",
@@ -56510,7 +56620,7 @@ var require_alerts_addon = __commonJS({
56510
56620
  [Symbol.toStringTag]: { value: "Module" }
56511
56621
  });
56512
56622
  require_chunk_Cek0wNdY();
56513
- var require_dist10 = require_dist_C2_1HCpW();
56623
+ var require_dist10 = require_dist_DnhGRFEn();
56514
56624
  function selectExpired(alerts, cutoffMs) {
56515
56625
  return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
56516
56626
  }
@@ -57329,7 +57439,7 @@ var require_console_logging = __commonJS({
57329
57439
  [Symbol.toStringTag]: { value: "Module" }
57330
57440
  });
57331
57441
  require_chunk_Cek0wNdY();
57332
- var require_dist10 = require_dist_C2_1HCpW();
57442
+ var require_dist10 = require_dist_DnhGRFEn();
57333
57443
  var require_formatter = require_formatter_DqAKDlvN();
57334
57444
  var LEVEL_RANK = {
57335
57445
  debug: 0,
@@ -57423,7 +57533,7 @@ var require_core_blocks_addon = __commonJS({
57423
57533
  "use strict";
57424
57534
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
57425
57535
  var require_chunk = require_chunk_Cek0wNdY();
57426
- var require_dist10 = require_dist_C2_1HCpW();
57536
+ var require_dist10 = require_dist_DnhGRFEn();
57427
57537
  var node_crypto = __require("crypto");
57428
57538
  var node_fs_promises = __require("fs/promises");
57429
57539
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -58320,11 +58430,11 @@ var require_core_blocks = __commonJS({
58320
58430
  }
58321
58431
  });
58322
58432
 
58323
- // ../system/dist/retired-settings-keys-DHRXLMPn.js
58324
- var require_retired_settings_keys_DHRXLMPn = __commonJS({
58325
- "../system/dist/retired-settings-keys-DHRXLMPn.js"(exports) {
58433
+ // ../system/dist/retired-settings-keys-D6Jy_SsO.js
58434
+ var require_retired_settings_keys_D6Jy_SsO = __commonJS({
58435
+ "../system/dist/retired-settings-keys-D6Jy_SsO.js"(exports) {
58326
58436
  "use strict";
58327
- var require_dist10 = require_dist_C2_1HCpW();
58437
+ var require_dist10 = require_dist_DnhGRFEn();
58328
58438
  function settingsStoreIsAuthoritativeHere(env) {
58329
58439
  const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
58330
58440
  return raw === "" || raw === "hub";
@@ -60367,8 +60477,8 @@ var require_device_manager_addon = __commonJS({
60367
60477
  [Symbol.toStringTag]: { value: "Module" }
60368
60478
  });
60369
60479
  require_chunk_Cek0wNdY();
60370
- var require_dist10 = require_dist_C2_1HCpW();
60371
- var require_retired_settings_keys = require_retired_settings_keys_DHRXLMPn();
60480
+ var require_dist10 = require_dist_DnhGRFEn();
60481
+ var require_retired_settings_keys = require_retired_settings_keys_D6Jy_SsO();
60372
60482
  var node_crypto = __require("crypto");
60373
60483
  var _camstack_types_node = require_node();
60374
60484
  var JOB_HISTORY = 20;
@@ -64695,7 +64805,7 @@ var require_hub_forwarder = __commonJS({
64695
64805
  [Symbol.toStringTag]: { value: "Module" }
64696
64806
  });
64697
64807
  require_chunk_Cek0wNdY();
64698
- var require_dist10 = require_dist_C2_1HCpW();
64808
+ var require_dist10 = require_dist_DnhGRFEn();
64699
64809
  var require_formatter = require_formatter_DqAKDlvN();
64700
64810
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
64701
64811
  var HubForwarderDestination = class {
@@ -64832,7 +64942,7 @@ var require_liveness_monitor_addon = __commonJS({
64832
64942
  "use strict";
64833
64943
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
64834
64944
  require_chunk_Cek0wNdY();
64835
- var require_dist10 = require_dist_C2_1HCpW();
64945
+ var require_dist10 = require_dist_DnhGRFEn();
64836
64946
  var NO_DEVICES = "liveness:no-devices";
64837
64947
  var ALL_OFFLINE = "liveness:all-devices-offline";
64838
64948
  var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
@@ -65022,7 +65132,7 @@ var require_local_auth_addon = __commonJS({
65022
65132
  [Symbol.toStringTag]: { value: "Module" }
65023
65133
  });
65024
65134
  var require_chunk = require_chunk_Cek0wNdY();
65025
- var require_dist10 = require_dist_C2_1HCpW();
65135
+ var require_dist10 = require_dist_DnhGRFEn();
65026
65136
  var node_crypto = __require("crypto");
65027
65137
  node_crypto = require_chunk.__toESM(node_crypto);
65028
65138
  var crypto$1 = __require("crypto");
@@ -72706,7 +72816,7 @@ var require_loki_logging = __commonJS({
72706
72816
  [Symbol.toStringTag]: { value: "Module" }
72707
72817
  });
72708
72818
  require_chunk_Cek0wNdY();
72709
- var require_dist10 = require_dist_C2_1HCpW();
72819
+ var require_dist10 = require_dist_DnhGRFEn();
72710
72820
  function sanitizeLabelName(raw) {
72711
72821
  const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
72712
72822
  return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
@@ -73271,7 +73381,7 @@ var require_native_metrics_addon = __commonJS({
73271
73381
  [Symbol.toStringTag]: { value: "Module" }
73272
73382
  });
73273
73383
  var require_chunk = require_chunk_Cek0wNdY();
73274
- var require_dist10 = require_dist_C2_1HCpW();
73384
+ var require_dist10 = require_dist_DnhGRFEn();
73275
73385
  var node_child_process = __require("child_process");
73276
73386
  var node_util = __require("util");
73277
73387
  var node_os = __require("os");
@@ -74213,7 +74323,7 @@ var require_filesystem_storage_addon = __commonJS({
74213
74323
  [Symbol.toStringTag]: { value: "Module" }
74214
74324
  });
74215
74325
  var require_chunk = require_chunk_Cek0wNdY();
74216
- var require_dist10 = require_dist_C2_1HCpW();
74326
+ var require_dist10 = require_dist_DnhGRFEn();
74217
74327
  var node_crypto = __require("crypto");
74218
74328
  var node_fs_promises = __require("fs/promises");
74219
74329
  var node_path = __require("path");
@@ -75329,8 +75439,8 @@ var require_sqlite_settings_addon = __commonJS({
75329
75439
  [Symbol.toStringTag]: { value: "Module" }
75330
75440
  });
75331
75441
  var require_chunk = require_chunk_Cek0wNdY();
75332
- var require_dist10 = require_dist_C2_1HCpW();
75333
- var require_retired_settings_keys = require_retired_settings_keys_DHRXLMPn();
75442
+ var require_dist10 = require_dist_DnhGRFEn();
75443
+ var require_retired_settings_keys = require_retired_settings_keys_D6Jy_SsO();
75334
75444
  var node_crypto = __require("crypto");
75335
75445
  var node_fs = __require("fs");
75336
75446
  var node_module = __require("module");
@@ -76674,6 +76784,17 @@ var require_sqlite_settings_addon = __commonJS({
76674
76784
  constructor() {
76675
76785
  super({});
76676
76786
  }
76787
+ /**
76788
+ * The engine behind `settings-store` cannot read that door during
76789
+ * `initialize()`. `BaseAddon` always `await`s `resolveConfig()` first;
76790
+ * on an isolated sqlite that round-trip is a UDS call to the parent,
76791
+ * and the parent is waiting for THIS child's post-init handshake before
76792
+ * it builds the door (D233). Live 1.2.152: 30 s hang, timeout, every
76793
+ * in-process builtin skipped, then this addon finished one second later.
76794
+ * Constructor defaults (`{}`) are the whole config this addon has.
76795
+ */
76796
+ async resolveConfig() {
76797
+ }
76677
76798
  async onInitialize() {
76678
76799
  const addonId = require_dist10.bareAddonId(this.ctx.id);
76679
76800
  const path = await import("path");
@@ -76789,6 +76910,7 @@ var require_sqlite_settings_addon = __commonJS({
76789
76910
  exports.WAL_IDLE_QUIET_MS = WAL_IDLE_QUIET_MS;
76790
76911
  exports.WAL_MAINTENANCE_INTERVAL_MS = WAL_MAINTENANCE_INTERVAL_MS;
76791
76912
  exports.WalMaintenance = WalMaintenance;
76913
+ exports.prefixRange = prefixRange;
76792
76914
  }
76793
76915
  });
76794
76916
 
@@ -76874,7 +76996,7 @@ var require_storage_orchestrator_addon = __commonJS({
76874
76996
  [Symbol.toStringTag]: { value: "Module" }
76875
76997
  });
76876
76998
  var require_chunk = require_chunk_Cek0wNdY();
76877
- var require_dist10 = require_dist_C2_1HCpW();
76999
+ var require_dist10 = require_dist_DnhGRFEn();
76878
77000
  var node_crypto = __require("crypto");
76879
77001
  var node_fs_promises = __require("fs/promises");
76880
77002
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -78753,7 +78875,7 @@ var require_system_config_addon = __commonJS({
78753
78875
  [Symbol.toStringTag]: { value: "Module" }
78754
78876
  });
78755
78877
  require_chunk_Cek0wNdY();
78756
- var require_dist10 = require_dist_C2_1HCpW();
78878
+ var require_dist10 = require_dist_DnhGRFEn();
78757
78879
  var SECTION_TITLES = {
78758
78880
  server: "Server",
78759
78881
  auth: "Authentication"
@@ -96814,7 +96936,7 @@ var require_winston_logging = __commonJS({
96814
96936
  [Symbol.toStringTag]: { value: "Module" }
96815
96937
  });
96816
96938
  var require_chunk = require_chunk_Cek0wNdY();
96817
- var require_dist10 = require_dist_C2_1HCpW();
96939
+ var require_dist10 = require_dist_DnhGRFEn();
96818
96940
  var require_formatter = require_formatter_DqAKDlvN();
96819
96941
  var node_path = __require("path");
96820
96942
  node_path = require_chunk.__toESM(node_path);
@@ -98011,9 +98133,9 @@ var require_event_category_EY0GNjV9 = __commonJS({
98011
98133
  }
98012
98134
  });
98013
98135
 
98014
- // ../types/dist/sleep-C2XhJhkd.js
98015
- var require_sleep_C2XhJhkd = __commonJS({
98016
- "../types/dist/sleep-C2XhJhkd.js"(exports) {
98136
+ // ../types/dist/sleep-CizGYrCD.js
98137
+ var require_sleep_CizGYrCD = __commonJS({
98138
+ "../types/dist/sleep-CizGYrCD.js"(exports) {
98017
98139
  "use strict";
98018
98140
  var require_event_category = require_event_category_EY0GNjV9();
98019
98141
  var zod = require_zod();
@@ -98384,6 +98506,13 @@ var require_sleep_C2XhJhkd = __commonJS({
98384
98506
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
98385
98507
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
98386
98508
  _registeredCapNames = [];
98509
+ /**
98510
+ * True only after `readAddonStore` actually answered. Constructor
98511
+ * defaults look like stored config when the store is down — a forked
98512
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
98513
+ * mode, 2026-08-25) is not "the operator chose this".
98514
+ */
98515
+ settingsStoreReady = false;
98387
98516
  /** Default config values. Provided via constructor. */
98388
98517
  defaults;
98389
98518
  constructor(defaults) {
@@ -98789,7 +98918,9 @@ var require_sleep_C2XhJhkd = __commonJS({
98789
98918
  ];
98790
98919
  let lastErr;
98791
98920
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
98792
- return await settings.readAddonStore() ?? {};
98921
+ const stored = await settings.readAddonStore() ?? {};
98922
+ this.settingsStoreReady = true;
98923
+ return stored;
98793
98924
  } catch (err) {
98794
98925
  lastErr = err;
98795
98926
  const msg = err instanceof Error ? err.message : String(err);
@@ -98797,6 +98928,7 @@ var require_sleep_C2XhJhkd = __commonJS({
98797
98928
  if (attempt === delaysMs.length) break;
98798
98929
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
98799
98930
  }
98931
+ this.settingsStoreReady = false;
98800
98932
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries \u2014 using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
98801
98933
  return {};
98802
98934
  }
@@ -100055,6 +100187,73 @@ var require_sleep_C2XhJhkd = __commonJS({
100055
100187
  }
100056
100188
  };
100057
100189
  }
100190
+ function createEventBusSliceSource(deps) {
100191
+ const { eventBus, api } = deps;
100192
+ const cache2 = /* @__PURE__ */ new Map();
100193
+ const listeners = /* @__PURE__ */ new Map();
100194
+ let offBus = null;
100195
+ const keyOf = (deviceId, capName) => `${deviceId}:${capName}`;
100196
+ const fanOut = (deviceId, capName, slice) => {
100197
+ const k = keyOf(deviceId, capName);
100198
+ cache2.set(k, slice);
100199
+ const set = listeners.get(k);
100200
+ if (!set) return;
100201
+ for (const cb of set) try {
100202
+ cb(slice);
100203
+ } catch {
100204
+ }
100205
+ };
100206
+ const ensureBridge = () => {
100207
+ if (offBus) return;
100208
+ offBus = eventBus.subscribe({ category: DEVICE_STATE_EVENT_CATEGORY }, (event2) => {
100209
+ const data = event2.data;
100210
+ if (typeof data !== "object" || data === null) return;
100211
+ const deviceId = Reflect.get(data, "deviceId");
100212
+ const capName = Reflect.get(data, "capName");
100213
+ if (typeof deviceId !== "number" || typeof capName !== "string") return;
100214
+ fanOut(deviceId, capName, Reflect.get(data, "slice"));
100215
+ });
100216
+ };
100217
+ const closeBridgeIfIdle = () => {
100218
+ if (offBus === null) return;
100219
+ if (listeners.size > 0) return;
100220
+ offBus();
100221
+ offBus = null;
100222
+ };
100223
+ return {
100224
+ read(deviceId, capName) {
100225
+ return cache2.get(keyOf(deviceId, capName));
100226
+ },
100227
+ async refresh(deviceId, capName) {
100228
+ fanOut(deviceId, capName, await api.deviceState.getCapSlice.query({
100229
+ deviceId,
100230
+ capName
100231
+ }) ?? void 0);
100232
+ },
100233
+ watch(deviceId, capName, cb) {
100234
+ const k = keyOf(deviceId, capName);
100235
+ let set = listeners.get(k);
100236
+ if (!set) {
100237
+ set = /* @__PURE__ */ new Set();
100238
+ listeners.set(k, set);
100239
+ }
100240
+ set.add(cb);
100241
+ ensureBridge();
100242
+ return () => {
100243
+ set.delete(cb);
100244
+ if (set.size === 0) listeners.delete(k);
100245
+ closeBridgeIfIdle();
100246
+ };
100247
+ },
100248
+ async write(deviceId, capName, slice) {
100249
+ await api.deviceState.setCapSlice.mutate({
100250
+ deviceId,
100251
+ capName,
100252
+ slice
100253
+ });
100254
+ }
100255
+ };
100256
+ }
100058
100257
  function createMirrorSource(mirror, listeners, api) {
100059
100258
  const keyOf = (deviceId, capName) => `${deviceId}:${capName}`;
100060
100259
  return {
@@ -100474,6 +100673,8 @@ var require_sleep_C2XhJhkd = __commonJS({
100474
100673
  getTrack: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getTrack", "query", input),
100475
100674
  listTracks: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listTracks", "query", input),
100476
100675
  listRecentTracks: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listRecentTracks", "query", input),
100676
+ listGroups: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listGroups", "query", input),
100677
+ getGroup: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getGroup", "query", input),
100477
100678
  clearTracks: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "clearTracks", "mutation", input),
100478
100679
  getMotionEvents: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getMotionEvents", "query", input),
100479
100680
  getObjectEvents: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getObjectEvents", "query", input),
@@ -101252,6 +101453,12 @@ var require_sleep_C2XhJhkd = __commonJS({
101252
101453
  return createEvent;
101253
101454
  }
101254
101455
  });
101456
+ Object.defineProperty(exports, "createEventBusSliceSource", {
101457
+ enumerable: true,
101458
+ get: function() {
101459
+ return createEventBusSliceSource;
101460
+ }
101461
+ });
101255
101462
  Object.defineProperty(exports, "createLazyTrpcSource", {
101256
101463
  enumerable: true,
101257
101464
  get: function() {
@@ -101453,7 +101660,7 @@ var require_addon = __commonJS({
101453
101660
  "use strict";
101454
101661
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
101455
101662
  var require_event_category = require_event_category_EY0GNjV9();
101456
- var require_sleep = require_sleep_C2XhJhkd();
101663
+ var require_sleep = require_sleep_CizGYrCD();
101457
101664
  var require_err_msg = require_err_msg_COpsHMw2();
101458
101665
  var CAP_INPUT_DEFAULTS = Object.freeze({
101459
101666
  "addons": { "getLogs": { "limit": 100 } },
@@ -101566,6 +101773,7 @@ var require_addon = __commonJS({
101566
101773
  "getMotionEvents": { "limit": 1e3 },
101567
101774
  "getObjectEvents": { "limit": 1e3 },
101568
101775
  "getSensorEvents": { "limit": 1e3 },
101776
+ "listGroups": { "limit": 40 },
101569
101777
  "listRecentTracks": { "limit": 200 },
101570
101778
  "searchObjectEvents": {
101571
101779
  "limit": 50,
@@ -101773,6 +101981,7 @@ var require_addon = __commonJS({
101773
101981
  exports.asJsonObject = require_sleep.asJsonObject;
101774
101982
  exports.asString = require_sleep.asString;
101775
101983
  exports.createDeviceProxy = require_sleep.createDeviceProxy;
101984
+ exports.createEventBusSliceSource = require_sleep.createEventBusSliceSource;
101776
101985
  exports.deviceOpsCapability = require_sleep.deviceOpsCapability;
101777
101986
  exports.emitReadiness = require_sleep.emitReadiness;
101778
101987
  exports.errMsg = require_err_msg.errMsg;
@@ -108327,9 +108536,9 @@ var require_dist2 = __commonJS({
108327
108536
  }
108328
108537
  });
108329
108538
 
108330
- // ../system/dist/manifest-python-deps-CwBbX4Ut.js
108331
- var require_manifest_python_deps_CwBbX4Ut = __commonJS({
108332
- "../system/dist/manifest-python-deps-CwBbX4Ut.js"(exports) {
108539
+ // ../system/dist/manifest-python-deps-B3_4YiDK.js
108540
+ var require_manifest_python_deps_B3_4YiDK = __commonJS({
108541
+ "../system/dist/manifest-python-deps-B3_4YiDK.js"(exports) {
108333
108542
  "use strict";
108334
108543
  var require_chunk = require_chunk_Cek0wNdY();
108335
108544
  var node_crypto = __require("crypto");
@@ -108345,6 +108554,7 @@ var require_manifest_python_deps_CwBbX4Ut = __commonJS({
108345
108554
  var node_fs = __require("fs");
108346
108555
  node_fs = require_chunk.__toESM(node_fs);
108347
108556
  var node_http = __require("http");
108557
+ var node_perf_hooks = __require("perf_hooks");
108348
108558
  var node_v8 = __require("v8");
108349
108559
  node_v8 = require_chunk.__toESM(node_v8);
108350
108560
  var node_vm = __require("vm");
@@ -108362,6 +108572,41 @@ var require_manifest_python_deps_CwBbX4Ut = __commonJS({
108362
108572
  }
108363
108573
  var HEAP_RECLAIM_TRIGGER_MB = 1024;
108364
108574
  var HEAP_RECLAIM_MIN_INTERVAL_MS = 12e4;
108575
+ var ZERO_LOOP_DELAY = {
108576
+ p50Ms: 0,
108577
+ p99Ms: 0,
108578
+ maxMs: 0
108579
+ };
108580
+ function createLoopDelayMeter(resolutionMs = 20) {
108581
+ try {
108582
+ const histogram = (0, node_perf_hooks.monitorEventLoopDelay)({ resolution: resolutionMs });
108583
+ histogram.enable();
108584
+ return {
108585
+ read: () => {
108586
+ try {
108587
+ if (histogram.count === 0) return ZERO_LOOP_DELAY;
108588
+ const sample = {
108589
+ p50Ms: Math.round(histogram.percentile(50) / 1e6),
108590
+ p99Ms: Math.round(histogram.percentile(99) / 1e6),
108591
+ maxMs: Math.round(histogram.max / 1e6)
108592
+ };
108593
+ histogram.reset();
108594
+ return sample;
108595
+ } catch {
108596
+ return ZERO_LOOP_DELAY;
108597
+ }
108598
+ },
108599
+ stop: () => {
108600
+ histogram.disable();
108601
+ }
108602
+ };
108603
+ } catch {
108604
+ return;
108605
+ }
108606
+ }
108607
+ function heapCeilingOrigin(execArgv = process.execArgv) {
108608
+ return execArgv.some((arg) => /^--max[-_]old[-_]space[-_]size(=|$)/.test(arg)) ? "explicit" : "v8-default";
108609
+ }
108365
108610
  var MB = (bytes) => Math.round(bytes / 1048576);
108366
108611
  function buildHeapSample(mem, heapLimitBytes, warnRatio = HEAP_WATCH_WARN_RATIO) {
108367
108612
  const usedRatio = heapLimitBytes > 0 ? mem.heapUsed / heapLimitBytes : 0;
@@ -108403,10 +108648,12 @@ var require_manifest_python_deps_CwBbX4Ut = __commonJS({
108403
108648
  return;
108404
108649
  }
108405
108650
  }
108406
- function format2(label, s) {
108407
- return `[mem] ${label} rss=${s.rssMb}MB heapUsed=${s.heapUsedMb}MB heapTotal=${s.heapTotalMb}MB heapLimit=${s.heapLimitMb}MB used=${Math.round(s.usedRatio * 100)}% external=${s.externalMb}MB arrayBuffers=${s.arrayBuffersMb}MB`;
108651
+ function format2(label, s, loop) {
108652
+ const line = `[mem] ${label} rss=${s.rssMb}MB heapUsed=${s.heapUsedMb}MB heapTotal=${s.heapTotalMb}MB heapLimit=${s.heapLimitMb}MB used=${Math.round(s.usedRatio * 100)}% external=${s.externalMb}MB arrayBuffers=${s.arrayBuffersMb}MB`;
108653
+ if (loop === void 0) return line;
108654
+ return `${line} loopP50=${loop.p50Ms}ms loopP99=${loop.p99Ms}ms loopMax=${loop.maxMs}ms`;
108408
108655
  }
108409
- function startHeapWatch(label = "hub-main", sink = consoleSink, intervalMs = HEAP_WATCH_INTERVAL_MS, reclaimOptions) {
108656
+ function startHeapWatch(label = "hub-main", sink = consoleSink, intervalMs = HEAP_WATCH_INTERVAL_MS, reclaimOptions, loopDelay = createLoopDelayMeter(), execArgv = process.execArgv) {
108410
108657
  const readMemory = reclaimOptions?.readMemory ?? (() => process.memoryUsage());
108411
108658
  const now = reclaimOptions?.now ?? (() => Date.now());
108412
108659
  const triggerMb = reclaimOptions?.triggerMb ?? 1024;
@@ -108415,14 +108662,22 @@ var require_manifest_python_deps_CwBbX4Ut = __commonJS({
108415
108662
  const escalateRatio = reclaimOptions?.escalateRatio ?? 0.7;
108416
108663
  const deescalateRatio = reclaimOptions?.deescalateRatio ?? 0.6;
108417
108664
  let lastReclaimAt = Number.NEGATIVE_INFINITY;
108665
+ let passesAtFloor = 0;
108666
+ let steadyStateAnnounced = false;
108418
108667
  const read = () => {
108419
108668
  const limit = reclaimOptions?.heapLimitBytes ?? node_v8.getHeapStatistics().heap_size_limit;
108420
108669
  return buildHeapSample(readMemory(), limit);
108421
108670
  };
108422
108671
  const maybeReclaim = (sample) => {
108423
108672
  if (reclaimOptions === void 0) return;
108424
- if (!shouldReclaim(sample, triggerMb)) return;
108425
- if (now() - lastReclaimAt < minIntervalMs) return;
108673
+ if (!shouldReclaim(sample, triggerMb)) {
108674
+ passesAtFloor = 0;
108675
+ steadyStateAnnounced = false;
108676
+ return;
108677
+ }
108678
+ const sinceLast = now() - lastReclaimAt;
108679
+ if (sinceLast < minIntervalMs) return;
108680
+ passesAtFloor = sinceLast <= minIntervalMs * 2 ? passesAtFloor + 1 : 1;
108426
108681
  lastReclaimAt = now();
108427
108682
  const startedAt = now();
108428
108683
  try {
@@ -108433,6 +108688,10 @@ var require_manifest_python_deps_CwBbX4Ut = __commonJS({
108433
108688
  }
108434
108689
  const after = read();
108435
108690
  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`);
108691
+ if (passesAtFloor >= 6 && !steadyStateAnnounced) {
108692
+ steadyStateAnnounced = true;
108693
+ sink.warn(`[mem] reclaim ${label} has run at the floor for ${passesAtFloor} consecutive passes (every ${minIntervalMs}ms) \u2014 the stop-the-world is now STEADY STATE, not a rescue. The allocation source is what needs the fix; this pass only bounds its peak.`);
108694
+ }
108436
108695
  };
108437
108696
  let mode = "steady";
108438
108697
  let lastLoggedAt = Number.NEGATIVE_INFINITY;
@@ -108446,7 +108705,7 @@ var require_manifest_python_deps_CwBbX4Ut = __commonJS({
108446
108705
  const due = at - lastLoggedAt >= intervalMs;
108447
108706
  if (mode === "escalated" || due) {
108448
108707
  lastLoggedAt = at;
108449
- const line = format2(label, sample);
108708
+ const line = format2(label, sample, loopDelay?.read());
108450
108709
  if (sample.nearLimit) sink.warn(`${line} \u2014 APPROACHING HEAP LIMIT`);
108451
108710
  else if (mode === "escalated") sink.warn(`${line} \u2014 heap elevated, sampling every ${probeIntervalMs}ms`);
108452
108711
  else sink.info(line);
@@ -108459,11 +108718,13 @@ var require_manifest_python_deps_CwBbX4Ut = __commonJS({
108459
108718
  const timer = setInterval(tick, probeIntervalMs);
108460
108719
  timer.unref?.();
108461
108720
  tick();
108721
+ if (heapCeilingOrigin(execArgv) === "v8-default") sink.info(`[mem] ${label} heap ceiling is V8's DEFAULT (${read().heapLimitMb}MB) \u2014 no --max-old-space-size on argv. Nothing here CHOSE that number; it is derived from host RAM and moves with it.`);
108462
108722
  let stopped = false;
108463
108723
  return () => {
108464
108724
  if (stopped) return;
108465
108725
  stopped = true;
108466
108726
  clearInterval(timer);
108727
+ loopDelay?.stop();
108467
108728
  };
108468
108729
  }
108469
108730
  var RUNNER_HEAP_WATCH_INTERVAL_MS = 3e5;
@@ -109555,6 +109816,10 @@ var require_manifest_python_deps_CwBbX4Ut = __commonJS({
109555
109816
  }
109556
109817
  function createBrokerDeviceManagerApi(opts) {
109557
109818
  const { api, addonId, nodeId, eventBus, registry } = opts;
109819
+ const deviceSliceSource = (0, _camstack_types_addon.createEventBusSliceSource)({
109820
+ eventBus,
109821
+ api
109822
+ });
109558
109823
  let selfApi;
109559
109824
  const deviceRebuildFactories = /* @__PURE__ */ new Map();
109560
109825
  const buildContext = (stableId, id, parentDeviceId = null, initialRuntimeState = {}, persistedConfig = {}, deviceMeta = null) => {
@@ -109599,7 +109864,7 @@ var require_manifest_python_deps_CwBbX4Ut = __commonJS({
109599
109864
  metadata: null
109600
109865
  },
109601
109866
  fetchDevice: async (deviceId) => {
109602
- return (0, _camstack_types_addon.createDeviceProxy)(api, await api.deviceManager.getBindings.query({ deviceId }));
109867
+ return (0, _camstack_types_addon.createDeviceProxy)(api, await api.deviceManager.getBindings.query({ deviceId }), { stateSource: deviceSliceSource });
109603
109868
  },
109604
109869
  get devices() {
109605
109870
  return selfApi;
@@ -114498,6 +114763,10 @@ var require_manifest_python_deps_CwBbX4Ut = __commonJS({
114498
114763
  const api = (0, _trpc_client.createTRPCClient)({ links });
114499
114764
  const scopedLogger = options?.createLogger?.(addonId) ?? (runtime.mode === "broker" ? createRemoteLogger(runtime.broker, addonId) : createUdsLogger(runtime.client, addonId, nodeId));
114500
114765
  const scopedEventBus = runtime.mode === "broker" ? createBrokerEventBus(runtime.broker, addonId) : createUdsEventBus(runtime.client, addonId);
114766
+ const deviceSliceSource = (0, _camstack_types_addon.createEventBusSliceSource)({
114767
+ eventBus: scopedEventBus,
114768
+ api
114769
+ });
114501
114770
  const workerDisposerChain = new _camstack_types_addon.DisposerChain({ onError: (err, index) => {
114502
114771
  scopedLogger.error(`Disposer #${index} threw during teardown`, { meta: { error: err instanceof Error ? err.message : String(err) } });
114503
114772
  } });
@@ -114733,10 +115002,10 @@ var require_manifest_python_deps_CwBbX4Ut = __commonJS({
114733
115002
  },
114734
115003
  fetchDevice: async (deviceId) => {
114735
115004
  const cached = bindingCache.get(deviceId);
114736
- if (cached) return (0, _camstack_types_addon.createDeviceProxy)(api, cached);
115005
+ if (cached) return (0, _camstack_types_addon.createDeviceProxy)(api, cached, { stateSource: deviceSliceSource });
114737
115006
  const binding = await api.deviceManager.getBindings.query({ deviceId });
114738
115007
  bindingCache.set(deviceId, binding);
114739
- return (0, _camstack_types_addon.createDeviceProxy)(api, binding);
115008
+ return (0, _camstack_types_addon.createDeviceProxy)(api, binding, { stateSource: deviceSliceSource });
114740
115009
  },
114741
115010
  useCapability(capName, scope = { type: "global" }) {
114742
115011
  return getOrCreateHandle(capName, scope, Number.POSITIVE_INFINITY);
@@ -119879,7 +120148,7 @@ var require_dist3 = __commonJS({
119879
120148
  "use strict";
119880
120149
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
119881
120150
  var require_chunk = require_chunk_Cek0wNdY();
119882
- var require_dist10 = require_dist_C2_1HCpW();
120151
+ var require_dist10 = require_dist_DnhGRFEn();
119883
120152
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
119884
120153
  require_alerts();
119885
120154
  var require_formatter = require_formatter_DqAKDlvN();
@@ -119904,7 +120173,7 @@ var require_dist3 = __commonJS({
119904
120173
  require_system_config();
119905
120174
  var require_builtins_winston_logging_index = require_winston_logging();
119906
120175
  var require_file_data_plane = require_file_data_plane_DO8KbxCe();
119907
- var require_manifest_python_deps = require_manifest_python_deps_CwBbX4Ut();
120176
+ var require_manifest_python_deps = require_manifest_python_deps_B3_4YiDK();
119908
120177
  var require_resource_monitor = require_resource_monitor_CdnzxBLP();
119909
120178
  var require_lan_http_bind = require_lan_http_bind_DmgpFP6();
119910
120179
  var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
@@ -122128,7 +122397,7 @@ var require_dist3 = __commonJS({
122128
122397
  if (missing.length > 0) throw new Error(`${options.addonName} \u2014 required native module(s) missing a compiled .node after install (bundled-copy + prebuild-fetch both failed): ${missing.join(", ")}`);
122129
122398
  return results;
122130
122399
  }
122131
- function isRecord$1(value) {
122400
+ function isRecord$2(value) {
122132
122401
  return typeof value === "object" && value !== null && !Array.isArray(value);
122133
122402
  }
122134
122403
  var PACKAGE_JSON_LOOKUP_DEPTH = 4;
@@ -122168,9 +122437,9 @@ var require_dist3 = __commonJS({
122168
122437
  return import(`${(0, node_url.pathToFileURL)(entryPath).href}?v=${encodeURIComponent(bust)}`);
122169
122438
  }
122170
122439
  function toAddonPackageManifest(value) {
122171
- if (!isRecord$1(value)) return void 0;
122440
+ if (!isRecord$2(value)) return void 0;
122172
122441
  if (!Array.isArray(value.addons)) return void 0;
122173
- if (!value.addons.every((a) => isRecord$1(a) && typeof a.id === "string")) return void 0;
122442
+ if (!value.addons.every((a) => isRecord$2(a) && typeof a.id === "string")) return void 0;
122174
122443
  return value;
122175
122444
  }
122176
122445
  var noopLogger$1 = {
@@ -122312,7 +122581,7 @@ var require_dist3 = __commonJS({
122312
122581
  }
122313
122582
  if (!node_fs.existsSync(entryPath)) throw new Error(`Entry not found: ${entryPath}`);
122314
122583
  const modUnknown = await importAddonModuleFresh(entryPath);
122315
- const mod = isRecord$1(modUnknown) ? modUnknown : {};
122584
+ const mod = isRecord$2(modUnknown) ? modUnknown : {};
122316
122585
  const AddonClass = require_manifest_python_deps.resolveAddonClass(mod);
122317
122586
  if (!AddonClass) throw new Error(`No addon class in ${entryPath}`);
122318
122587
  this.addons.set(declaration.id, {
@@ -122328,7 +122597,7 @@ var require_dist3 = __commonJS({
122328
122597
  /** Load addon from a direct path (for development/testing) */
122329
122598
  async loadFromPath(addonId, modulePath, packageName, declaration, packageVersion = "0.0.0") {
122330
122599
  const modUnknown = await importAddonModuleFresh(modulePath);
122331
- const mod = isRecord$1(modUnknown) ? modUnknown : {};
122600
+ const mod = isRecord$2(modUnknown) ? modUnknown : {};
122332
122601
  const AddonClass = require_manifest_python_deps.resolveAddonClass(mod);
122333
122602
  if (!AddonClass) throw new Error(`Module ${modulePath} has no default export`);
122334
122603
  this.addons.set(addonId, {
@@ -125691,6 +125960,143 @@ var require_dist3 = __commonJS({
125691
125960
  function isInfraCapability(name2) {
125692
125961
  return infraNames.has(name2);
125693
125962
  }
125963
+ var INFRA_NAMES = new Set(INFRA_CAPABILITIES.map((c) => c.name));
125964
+ function isolatedBuiltinPhase(capabilities) {
125965
+ return capabilities.some((c) => INFRA_NAMES.has(c.name)) ? "infra" : "consumer";
125966
+ }
125967
+ function partitionIsolatedBuiltinIds(ids, capabilitiesOf) {
125968
+ const infra = [];
125969
+ const consumers = [];
125970
+ for (const id of ids) if (isolatedBuiltinPhase(capabilitiesOf(id)) === "infra") infra.push(id);
125971
+ else consumers.push(id);
125972
+ return {
125973
+ infra,
125974
+ consumers
125975
+ };
125976
+ }
125977
+ async function waitUntilReady(isReady, options) {
125978
+ const deadline = Date.now() + options.timeoutMs;
125979
+ for (; ; ) {
125980
+ if (isReady()) return;
125981
+ if (Date.now() >= deadline) throw new Error(`Timed out waiting for ${options.what}`);
125982
+ await new Promise((resolve) => {
125983
+ setTimeout(resolve, options.intervalMs);
125984
+ });
125985
+ }
125986
+ }
125987
+ async function runHubAddonBoot(steps) {
125988
+ await steps.spawnIsolatedInfra();
125989
+ await steps.waitForDataStoreProvider();
125990
+ await steps.bootInProcessInfra();
125991
+ await steps.spawnForkedAddons();
125992
+ await steps.bootInProcessConsumers();
125993
+ await steps.spawnIsolatedConsumers();
125994
+ }
125995
+ function isRecord$1(value) {
125996
+ return typeof value === "object" && value !== null && !Array.isArray(value);
125997
+ }
125998
+ function asBlob(value) {
125999
+ return isRecord$1(value) ? value : {};
126000
+ }
126001
+ function unwrapValue(parsed) {
126002
+ if (!isRecord$1(parsed) || !("value" in parsed)) return parsed;
126003
+ return parsed.value;
126004
+ }
126005
+ async function loadPrefixed(door, collection, prefix) {
126006
+ const range = require_builtins_sqlite_storage_sqlite_settings_addon.prefixRange(prefix);
126007
+ const rows = await door.query({
126008
+ collection,
126009
+ ...range !== null ? { filter: { whereBetween: { id: [range.lo, range.hi] } } } : {}
126010
+ });
126011
+ const result = {};
126012
+ for (const row of rows) {
126013
+ if (range !== null && (row.id < range.lo || row.id >= range.hi)) continue;
126014
+ if (!row.id.startsWith(prefix)) continue;
126015
+ result[row.id.slice(prefix.length)] = unwrapValue(row.data);
126016
+ }
126017
+ return result;
126018
+ }
126019
+ async function replacePrefixed(door, collection, prefix, values, wrap3) {
126020
+ const range = require_builtins_sqlite_storage_sqlite_settings_addon.prefixRange(prefix);
126021
+ if (range !== null) await door.deleteWhere({
126022
+ collection,
126023
+ filter: { whereBetween: { id: [range.lo, range.hi] } }
126024
+ });
126025
+ for (const [key, value] of Object.entries(values)) {
126026
+ if (value === void 0) continue;
126027
+ await door.set({
126028
+ collection,
126029
+ key: `${prefix}${key}`,
126030
+ value: wrap3(key, value)
126031
+ });
126032
+ }
126033
+ }
126034
+ function createDoorSettingsView(addonId, door, sections) {
126035
+ const addonPrefix = `${addonId}.`;
126036
+ return {
126037
+ async readAddonStore() {
126038
+ return loadPrefixed(door, "addon-settings", addonPrefix);
126039
+ },
126040
+ async writeAddonStore(patch) {
126041
+ await replacePrefixed(door, "addon-settings", addonPrefix, {
126042
+ ...await loadPrefixed(door, "addon-settings", addonPrefix),
126043
+ ...patch
126044
+ }, (key, value) => ({
126045
+ addonId,
126046
+ key,
126047
+ value
126048
+ }));
126049
+ },
126050
+ async readDeviceStore(deviceId) {
126051
+ return loadPrefixed(door, "addon-device-settings", `${addonId}:${String(deviceId)}.`);
126052
+ },
126053
+ async writeDeviceStore(deviceId, patch) {
126054
+ const scope = `${addonId}:${String(deviceId)}.`;
126055
+ await replacePrefixed(door, "addon-device-settings", scope, {
126056
+ ...await loadPrefixed(door, "addon-device-settings", scope),
126057
+ ...patch
126058
+ }, (key, value) => ({
126059
+ addonId,
126060
+ deviceId: String(deviceId),
126061
+ key,
126062
+ value
126063
+ }));
126064
+ },
126065
+ async clearDeviceStore(deviceId) {
126066
+ const range = require_builtins_sqlite_storage_sqlite_settings_addon.prefixRange(`${addonId}:${String(deviceId)}.`);
126067
+ if (range === null) return;
126068
+ await door.deleteWhere({
126069
+ collection: "addon-device-settings",
126070
+ filter: { whereBetween: { id: [range.lo, range.hi] } }
126071
+ });
126072
+ },
126073
+ async readDeviceRuntimeState(deviceId) {
126074
+ return asBlob(await door.get({
126075
+ collection: "device-runtime-state",
126076
+ key: String(deviceId)
126077
+ }));
126078
+ },
126079
+ async writeDeviceRuntimeState(deviceId, data) {
126080
+ await door.set({
126081
+ collection: "device-runtime-state",
126082
+ key: String(deviceId),
126083
+ value: data
126084
+ });
126085
+ },
126086
+ async clearDeviceRuntimeState(deviceId) {
126087
+ await door.delete({
126088
+ collection: "device-runtime-state",
126089
+ key: String(deviceId)
126090
+ });
126091
+ },
126092
+ async getSection(section) {
126093
+ return sections.getSection(section);
126094
+ },
126095
+ async setSection(section, patch) {
126096
+ await sections.setSection(section, patch);
126097
+ }
126098
+ };
126099
+ }
125694
126100
  var __create = Object.create;
125695
126101
  var __defProp = Object.defineProperty;
125696
126102
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -128170,6 +128576,7 @@ var require_dist3 = __commonJS({
128170
128576
  configPath;
128171
128577
  bootstrapConfig;
128172
128578
  settingsStore = null;
128579
+ settingsDoor = null;
128173
128580
  runtimeState;
128174
128581
  runtimeStatePath;
128175
128582
  constructor(configPath) {
@@ -128190,6 +128597,14 @@ var require_dist3 = __commonJS({
128190
128597
  setSettingsStore(store) {
128191
128598
  this.settingsStore = store;
128192
128599
  }
128600
+ /**
128601
+ * Wire the async `settings-store` door. Used when the engine is isolated
128602
+ * (no sync `ISettingsStore` handle on hub-main). {@link createSettingsView}
128603
+ * prefers the sync store when both are present.
128604
+ */
128605
+ setSettingsDoor(door) {
128606
+ this.settingsDoor = door;
128607
+ }
128193
128608
  get(configPath) {
128194
128609
  return this.resolveConfigValue(configPath);
128195
128610
  }
@@ -128210,8 +128625,21 @@ var require_dist3 = __commonJS({
128210
128625
  * Throws if the settings store is not yet wired.
128211
128626
  */
128212
128627
  set(key, value) {
128213
- if (this.settingsStore === null) throw new Error("[ConfigManager] SettingsStore not initialized -- call setSettingsStore() first");
128214
- this.settingsStore.setSystem(key, value);
128628
+ if (this.settingsStore !== null) {
128629
+ this.settingsStore.setSystem(key, value);
128630
+ return;
128631
+ }
128632
+ if (this.settingsDoor !== null) {
128633
+ this.settingsDoor.set({
128634
+ collection: "system-settings",
128635
+ key,
128636
+ value
128637
+ }).catch((err) => {
128638
+ console.error("[ConfigManager] isolated settings-store write failed", err);
128639
+ });
128640
+ return;
128641
+ }
128642
+ throw new Error("[ConfigManager] SettingsStore not initialized -- call setSettingsStore() first");
128215
128643
  }
128216
128644
  /**
128217
128645
  * Bulk-read all keys that belong to a logical section.
@@ -128332,6 +128760,19 @@ var require_dist3 = __commonJS({
128332
128760
  this.settingsStore.clearDeviceRuntimeState(deviceId);
128333
128761
  }
128334
128762
  createSettingsView(addonId) {
128763
+ if (this.settingsStore === null && this.settingsDoor !== null) {
128764
+ const door = this.settingsDoor;
128765
+ return createDoorSettingsView(addonId, door, {
128766
+ getSection: (section) => this.getSection(section),
128767
+ setSection: async (section, patch) => {
128768
+ for (const [key, value] of Object.entries(patch)) await door.set({
128769
+ collection: "system-settings",
128770
+ key: `${section}.${key}`,
128771
+ value
128772
+ });
128773
+ }
128774
+ });
128775
+ }
128335
128776
  const cm = this;
128336
128777
  return {
128337
128778
  async readAddonStore() {
@@ -200115,6 +200556,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
200115
200556
  exports.createBroker = createBroker2;
200116
200557
  exports.createBrokerDeviceManagerApi = require_manifest_python_deps.createBrokerDeviceManagerApi;
200117
200558
  exports.createCoreCapService = createCoreCapService;
200559
+ exports.createDoorSettingsView = createDoorSettingsView;
200118
200560
  exports.createFileDataPlaneHandler = require_file_data_plane.createFileDataPlaneHandler;
200119
200561
  exports.createHubCapForwardService = require_manifest_python_deps.createHubCapForwardService;
200120
200562
  exports.createHubService = createHubService;
@@ -200234,6 +200676,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
200234
200676
  exports.isInfraCapability = isInfraCapability;
200235
200677
  exports.isModelDownloaded = require_file_data_plane.isModelDownloaded;
200236
200678
  exports.isSourceNewer = isSourceNewer;
200679
+ exports.isolatedBuiltinPhase = isolatedBuiltinPhase;
200237
200680
  exports.loadTlsCert = require_lan_http_bind.loadTlsCert;
200238
200681
  exports.localEndpointPath = require_manifest_python_deps.localEndpointPath;
200239
200682
  exports.localProviderLink = require_manifest_python_deps.localProviderLink;
@@ -200241,6 +200684,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
200241
200684
  exports.parseCapAction = require_manifest_python_deps.parseCapAction;
200242
200685
  exports.parseRangeHeader = require_file_data_plane.parseRangeHeader;
200243
200686
  exports.parseTokenizedUrl = require_file_data_plane.parseTokenizedUrl;
200687
+ exports.partitionIsolatedBuiltinIds = partitionIsolatedBuiltinIds;
200244
200688
  exports.proxyToUpstream = proxyToUpstream;
200245
200689
  exports.quarantineAddonResidue = quarantineAddonResidue;
200246
200690
  exports.readExtraSans = require_lan_http_bind.readExtraSans;
@@ -200255,6 +200699,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
200255
200699
  exports.resolveFilePath = require_file_data_plane.resolveFilePath;
200256
200700
  exports.resolveHwAccel = require_manifest_python_deps.resolveHwAccel;
200257
200701
  exports.resolveNpmInvocation = require_manifest_python_deps.resolveNpmInvocation;
200702
+ exports.runHubAddonBoot = runHubAddonBoot;
200258
200703
  exports.runNpm = require_manifest_python_deps.runNpm;
200259
200704
  exports.scheduleSelfRestart = scheduleSelfRestart;
200260
200705
  exports.scopeKey = require_dist10.scopeKey;
@@ -200273,6 +200718,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
200273
200718
  exports.udsChildLogToWorkerEntry = require_manifest_python_deps.udsChildLogToWorkerEntry;
200274
200719
  exports.validateProviderRegistrations = require_manifest_python_deps.validateProviderRegistrations;
200275
200720
  exports.validateUploadedTls = require_lan_http_bind.validateUploadedTls;
200721
+ exports.waitUntilReady = waitUntilReady;
200276
200722
  exports.writeExtraSans = require_lan_http_bind.writeExtraSans;
200277
200723
  exports.writePendingRestart = writePendingRestart;
200278
200724
  exports.writeTlsMode = require_lan_http_bind.writeTlsMode;
@@ -238964,7 +239410,7 @@ var require_dist9 = __commonJS({
238964
239410
  "use strict";
238965
239411
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
238966
239412
  var require_event_category = require_event_category_EY0GNjV9();
238967
- var require_sleep = require_sleep_C2XhJhkd();
239413
+ var require_sleep = require_sleep_CizGYrCD();
238968
239414
  var require_canonical_hash = require_canonical_hash_DNV8S5ET();
238969
239415
  var require_enums2 = require_enums();
238970
239416
  var require_err_msg = require_err_msg_COpsHMw2();
@@ -240673,6 +241119,20 @@ var require_dist9 = __commonJS({
240673
241119
  */
240674
241120
  resolution: zod.z.number().int().positive().optional()
240675
241121
  });
241122
+ var MODEL_PROVIDER_IDS = [
241123
+ "camstack",
241124
+ "frigate",
241125
+ "scrypted",
241126
+ "custom"
241127
+ ];
241128
+ var ModelProviderIdSchema = zod.z.enum(MODEL_PROVIDER_IDS);
241129
+ function inferModelProvider(entry) {
241130
+ if (entry.provider !== void 0) return entry.provider;
241131
+ const haystack = `${entry.id} ${entry.description ?? ""}`;
241132
+ if (/scrypted/i.test(haystack)) return "scrypted";
241133
+ if (/frigate/i.test(haystack)) return "frigate";
241134
+ return "custom";
241135
+ }
240676
241136
  var ModelCatalogEntrySchema = zod.z.object({
240677
241137
  id: zod.z.string(),
240678
241138
  name: zod.z.string(),
@@ -240770,6 +241230,12 @@ var require_dist9 = __commonJS({
240770
241230
  */
240771
241231
  group: ModelVariantGroupSchema.optional(),
240772
241232
  /**
241233
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
241234
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
241235
+ * persisted before this field existed (`inferModelProvider` fills those).
241236
+ */
241237
+ provider: ModelProviderIdSchema.optional(),
241238
+ /**
240773
241239
  * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
240774
241240
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
240775
241241
  * labels already ARE the CamStack macros (Scrypted identity map).
@@ -248632,14 +249098,22 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
248632
249098
  });
248633
249099
  var NC_AUDIO_DBFS_FLOOR = -96;
248634
249100
  var NcAudioConditionSchema = zod.z.object({
248635
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
249101
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
248636
249102
  labels: zod.z.array(zod.z.string().min(1)).min(1).optional(),
248637
249103
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
248638
249104
  dbThreshold: zod.z.number().min(-96).max(0).optional(),
248639
249105
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
248640
249106
  hitPercent: zod.z.number().int().min(1).max(100).default(60),
248641
249107
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
248642
- samplingSeconds: zod.z.number().int().min(1).max(300).default(10)
249108
+ samplingSeconds: zod.z.number().int().min(1).max(300).default(10),
249109
+ /**
249110
+ * LABEL MODE: how many labelled frames must land inside
249111
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
249112
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
249113
+ */
249114
+ confirmHits: zod.z.number().int().min(1).max(20).optional(),
249115
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
249116
+ confirmWindowSec: zod.z.number().int().min(1).max(60).optional()
248643
249117
  });
248644
249118
  var NcCrossingSchema = zod.z.enum([
248645
249119
  "enter",
@@ -250899,6 +251373,46 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
250899
251373
  /** Cursor for the next page, or null when this page is the last. */
250900
251374
  nextCursor: zod.z.string().nullable()
250901
251375
  });
251376
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
251377
+ var LIST_GROUPS_MAX_LIMIT = 100;
251378
+ var AnalyticsGroupRecordSchema = zod.z.object({
251379
+ id: zod.z.string(),
251380
+ deviceId: zod.z.number().int(),
251381
+ openedAt: zod.z.number().int(),
251382
+ closedAt: zod.z.number().int(),
251383
+ timestamp: zod.z.number().int(),
251384
+ memberCount: zod.z.number().int(),
251385
+ memberTrackIds: zod.z.array(zod.z.string()).readonly(),
251386
+ className: zod.z.string(),
251387
+ classes: zod.z.array(zod.z.string()).readonly(),
251388
+ /** Relative event-media path, or null when the group has no picture yet. */
251389
+ mediaUrl: zod.z.string().nullable(),
251390
+ singleton: zod.z.boolean()
251391
+ });
251392
+ var AnalyticsGroupMemberSchema = zod.z.object({
251393
+ trackId: zod.z.string(),
251394
+ deviceId: zod.z.number().int(),
251395
+ className: zod.z.string(),
251396
+ firstSeen: zod.z.number().int(),
251397
+ lastSeen: zod.z.number().int(),
251398
+ mediaUrl: zod.z.string().nullable()
251399
+ });
251400
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: zod.z.array(AnalyticsGroupMemberSchema).readonly() });
251401
+ var ListGroupsQueryInput = zod.z.object({
251402
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
251403
+ deviceIds: zod.z.array(zod.z.number()),
251404
+ /** Window lower bound on `closedAt` (inclusive). */
251405
+ since: zod.z.number().optional(),
251406
+ /** Window upper bound on `openedAt` (inclusive). */
251407
+ until: zod.z.number().optional(),
251408
+ limit: zod.z.number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
251409
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
251410
+ cursor: zod.z.string().optional()
251411
+ });
251412
+ var ListGroupsPageSchema = zod.z.object({
251413
+ groups: zod.z.array(AnalyticsGroupRecordSchema).readonly(),
251414
+ nextCursor: zod.z.string().nullable()
251415
+ });
250902
251416
  var KeyEventQueryInput = zod.z.object({
250903
251417
  deviceId: zod.z.number(),
250904
251418
  /** Window lower bound (track firstSeen ≥ since). */
@@ -250974,7 +251488,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
250974
251488
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
250975
251489
  plates: zod.z.number().int(),
250976
251490
  /** Per-track CLIP search vectors removed (best-effort). */
250977
- embeddings: zod.z.number().int()
251491
+ embeddings: zod.z.number().int(),
251492
+ /** Group membership + group rows removed with their last member (best-effort). */
251493
+ groups: zod.z.number().int()
250978
251494
  });
250979
251495
  var DiskReconcileCountsSchema = zod.z.object({
250980
251496
  mediaDropped: zod.z.number().int(),
@@ -251116,6 +251632,16 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
251116
251632
  * are not included (same contract as `listTracks`).
251117
251633
  */
251118
251634
  listRecentTracks: require_sleep.method(RecentTracksQueryInput, RecentTracksPageSchema),
251635
+ /**
251636
+ * Batched co-moving group listing — the Groups feed. Same merge/cursor
251637
+ * contract as {@link listRecentTracks}. A group is a sealed partition of
251638
+ * one session; `getGroup` is the detail with members.
251639
+ */
251640
+ listGroups: require_sleep.method(ListGroupsQueryInput, ListGroupsPageSchema),
251641
+ getGroup: require_sleep.method(zod.z.object({
251642
+ deviceId: zod.z.number(),
251643
+ groupId: zod.z.string().min(1)
251644
+ }), AnalyticsGroupDetailSchema.nullable()),
251119
251645
  clearTracks: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.void(), {
251120
251646
  kind: "mutation",
251121
251647
  auth: "admin"
@@ -251797,7 +252323,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
251797
252323
  sizeMB: zod.z.number()
251798
252324
  })),
251799
252325
  group: ModelVariantGroupSchema.optional(),
251800
- legacy: zod.z.boolean().optional()
252326
+ legacy: zod.z.boolean().optional(),
252327
+ provider: ModelProviderIdSchema.optional()
251801
252328
  });
251802
252329
  var ConfigFieldBridge = zod.z.custom();
251803
252330
  var PipelineAddonSchemaSchema = zod.z.object({
@@ -260275,7 +260802,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
260275
260802
  plateBbox: BoundingBoxSchema.optional(),
260276
260803
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
260277
260804
  keyFrameMediaKey: zod.z.string().optional(),
260278
- base64: zod.z.string().optional()
260805
+ base64: zod.z.string().optional(),
260806
+ /**
260807
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
260808
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
260809
+ */
260810
+ cropUrl: zod.z.string().optional()
260279
260811
  });
260280
260812
  var MediaFileLiteSchema = zod.z.object({
260281
260813
  key: zod.z.string(),
@@ -269917,6 +270449,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
269917
270449
  addonId: null,
269918
270450
  access: "view"
269919
270451
  },
270452
+ "pipelineAnalytics.getGroup": {
270453
+ capName: "pipeline-analytics",
270454
+ capScope: "device",
270455
+ addonId: null,
270456
+ access: "view"
270457
+ },
269920
270458
  "pipelineAnalytics.getKeyEvents": {
269921
270459
  capName: "pipeline-analytics",
269922
270460
  capScope: "device",
@@ -270001,6 +270539,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
270001
270539
  addonId: null,
270002
270540
  access: "view"
270003
270541
  },
270542
+ "pipelineAnalytics.listGroups": {
270543
+ capName: "pipeline-analytics",
270544
+ capScope: "device",
270545
+ addonId: null,
270546
+ access: "view"
270547
+ },
270004
270548
  "pipelineAnalytics.listOpsLog": {
270005
270549
  capName: "pipeline-analytics",
270006
270550
  capScope: "device",
@@ -273672,6 +274216,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
273672
274216
  form: "single",
273673
274217
  optional: false
273674
274218
  }],
274219
+ "pipelineAnalytics.getGroup": [{
274220
+ name: "deviceId",
274221
+ form: "single",
274222
+ optional: false
274223
+ }],
273675
274224
  "pipelineAnalytics.getKeyEvents": [{
273676
274225
  name: "deviceId",
273677
274226
  form: "single",
@@ -273727,6 +274276,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
273727
274276
  form: "array",
273728
274277
  optional: false
273729
274278
  }],
274279
+ "pipelineAnalytics.listGroups": [{
274280
+ name: "deviceIds",
274281
+ form: "array",
274282
+ optional: false
274283
+ }],
273730
274284
  "pipelineAnalytics.listOpsLog": [{
273731
274285
  name: "deviceId",
273732
274286
  form: "single",
@@ -275613,6 +276167,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
275613
276167
  var NC_AUDIO_HIT_PERCENT_MAX = 100;
275614
276168
  var NC_AUDIO_SAMPLING_MIN_SEC = 1;
275615
276169
  var NC_AUDIO_SAMPLING_MAX_SEC = 300;
276170
+ var NC_AUDIO_CONFIRM_HITS_DEFAULT = 2;
276171
+ var NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT = 5;
276172
+ var NC_AUDIO_CONFIRM_HITS_MIN = 1;
276173
+ var NC_AUDIO_CONFIRM_HITS_MAX = 20;
276174
+ var NC_AUDIO_CONFIRM_WINDOW_MIN_SEC = 1;
276175
+ var NC_AUDIO_CONFIRM_WINDOW_MAX_SEC = 60;
275616
276176
  var NC_AUDIO_DEFAULTS = {
275617
276177
  hitPercent: 60,
275618
276178
  samplingSeconds: 10
@@ -275649,7 +276209,15 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
275649
276209
  ...labels !== void 0 ? { labels: [...labels] } : {},
275650
276210
  ...dbThreshold !== void 0 ? { dbThreshold: clampInt(dbThreshold, NC_AUDIO_DB_MIN, 0) } : {},
275651
276211
  hitPercent: clampInt(patch.hitPercent ?? base.hitPercent, 1, 100),
275652
- samplingSeconds: clampInt(patch.samplingSeconds ?? base.samplingSeconds, 1, 300)
276212
+ samplingSeconds: clampInt(patch.samplingSeconds ?? base.samplingSeconds, 1, 300),
276213
+ ...(() => {
276214
+ const hits = has(patch, "confirmHits") ? patch.confirmHits : base.confirmHits;
276215
+ const windowSec = has(patch, "confirmWindowSec") ? patch.confirmWindowSec : base.confirmWindowSec;
276216
+ return {
276217
+ ...hits !== void 0 ? { confirmHits: clampInt(hits, 1, 20) } : {},
276218
+ ...windowSec !== void 0 ? { confirmWindowSec: clampInt(windowSec, 1, 60) } : {}
276219
+ };
276220
+ })()
275653
276221
  };
275654
276222
  }
275655
276223
  function audioLabelChoices(taxonomy, selected) {
@@ -278233,6 +278801,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
278233
278801
  exports.AlertSourceSchema = AlertSourceSchema;
278234
278802
  exports.AlertStatusSchema = AlertStatusSchema;
278235
278803
  exports.AmbientLightSensorStatusSchema = AmbientLightSensorStatusSchema;
278804
+ exports.AnalyticsGroupDetailSchema = AnalyticsGroupDetailSchema;
278805
+ exports.AnalyticsGroupMemberSchema = AnalyticsGroupMemberSchema;
278806
+ exports.AnalyticsGroupRecordSchema = AnalyticsGroupRecordSchema;
278236
278807
  exports.ApiKeyRecordSchema = ApiKeyRecordSchema;
278237
278808
  exports.ApiKeySummarySchema = ApiKeySummarySchema;
278238
278809
  exports.ArchiveEntrySchema = ArchiveEntrySchema;
@@ -278576,6 +279147,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
278576
279147
  exports.LawnMowerControlStatusSchema = LawnMowerControlStatusSchema;
278577
279148
  exports.LinkedDeviceSchema = LinkedDeviceSchema;
278578
279149
  exports.LinkedDevicesModeSchema = LinkedDevicesModeSchema;
279150
+ exports.ListGroupsPageSchema = ListGroupsPageSchema;
279151
+ exports.ListGroupsQueryInput = ListGroupsQueryInput;
278579
279152
  exports.LlmDefaultSchema = LlmDefaultSchema;
278580
279153
  exports.LlmDefaultSelectorSchema = LlmDefaultSelectorSchema;
278581
279154
  exports.LlmDownloadProgressSchema = LlmDownloadProgressSchema;
@@ -278619,6 +279192,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
278619
279192
  exports.METHOD_ACCESS_MAP = METHOD_ACCESS_MAP;
278620
279193
  exports.METHOD_DEVICE_SELECTORS = METHOD_DEVICE_SELECTORS;
278621
279194
  exports.MODEL_FORMATS = MODEL_FORMATS;
279195
+ exports.MODEL_PROVIDER_IDS = MODEL_PROVIDER_IDS;
278622
279196
  exports.MOTION_TRIGGER_FEATURE = MOTION_TRIGGER_FEATURE;
278623
279197
  exports.ManagedModelCatalogEntrySchema = ManagedModelCatalogEntrySchema;
278624
279198
  exports.ManagedModelExtraFileSchema = ManagedModelExtraFileSchema;
@@ -278649,6 +279223,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
278649
279223
  exports.ModelExtraFileSchema = ModelExtraFileSchema;
278650
279224
  exports.ModelFormatEntrySchema = ModelFormatEntrySchema;
278651
279225
  exports.ModelFormatsSchema = ModelFormatsSchema;
279226
+ exports.ModelProviderIdSchema = ModelProviderIdSchema;
278652
279227
  exports.ModelSubstitutionSchema = ModelSubstitutionSchema;
278653
279228
  exports.ModelVariantGroupSchema = ModelVariantGroupSchema;
278654
279229
  exports.MotionAnalysisResultSchema = MotionAnalysisResultSchema;
@@ -278678,6 +279253,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
278678
279253
  exports.NATIVE_LEASE_TILE_BUDGET_FIELD = NATIVE_LEASE_TILE_BUDGET_FIELD;
278679
279254
  exports.NATIVE_LEASE_TILE_BUDGET_KEY = NATIVE_LEASE_TILE_BUDGET_KEY;
278680
279255
  exports.NC_ALARM_SYSTEM_EVENT_KINDS = NC_ALARM_SYSTEM_EVENT_KINDS;
279256
+ exports.NC_AUDIO_CONFIRM_HITS_DEFAULT = NC_AUDIO_CONFIRM_HITS_DEFAULT;
279257
+ exports.NC_AUDIO_CONFIRM_HITS_MAX = NC_AUDIO_CONFIRM_HITS_MAX;
279258
+ exports.NC_AUDIO_CONFIRM_HITS_MIN = NC_AUDIO_CONFIRM_HITS_MIN;
279259
+ exports.NC_AUDIO_CONFIRM_WINDOW_MAX_SEC = NC_AUDIO_CONFIRM_WINDOW_MAX_SEC;
279260
+ exports.NC_AUDIO_CONFIRM_WINDOW_MIN_SEC = NC_AUDIO_CONFIRM_WINDOW_MIN_SEC;
279261
+ exports.NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT = NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT;
278681
279262
  exports.NC_AUDIO_DBFS_FLOOR = NC_AUDIO_DBFS_FLOOR;
278682
279263
  exports.NC_AUDIO_DB_MAX = NC_AUDIO_DB_MAX;
278683
279264
  exports.NC_AUDIO_DB_MIN = NC_AUDIO_DB_MIN;
@@ -279216,6 +279797,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
279216
279797
  exports.createDeviceProxy = require_sleep.createDeviceProxy;
279217
279798
  exports.createDurableState = require_sleep.createDurableState;
279218
279799
  exports.createEvent = require_sleep.createEvent;
279800
+ exports.createEventBusSliceSource = require_sleep.createEventBusSliceSource;
279219
279801
  exports.createExpressionScope = createExpressionScope;
279220
279802
  exports.createHwAccelCache = createHwAccelCache;
279221
279803
  exports.createLazyTrpcSource = require_sleep.createLazyTrpcSource;
@@ -279298,6 +279880,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
279298
279880
  exports.hydrateSchema = require_sleep.hydrateSchema;
279299
279881
  exports.imageCapability = imageCapability;
279300
279882
  exports.imageSettingsCapability = imageSettingsCapability;
279883
+ exports.inferModelProvider = inferModelProvider;
279301
279884
  exports.initialPoolMemoryState = initialPoolMemoryState;
279302
279885
  exports.integrationsCapability = integrationsCapability;
279303
279886
  exports.intercomCapability = intercomCapability;
@@ -404548,90 +405131,127 @@ var require_addon_registry_service = __commonJS({
404548
405131
  return entry?.packageName === "@camstack/system" && entry.declaration !== void 0 && (0, types_1.isIsolatedBuiltin)(entry.declaration);
404549
405132
  });
404550
405133
  const isolatedBuiltins = new Set(isolatedBuiltinIds);
404551
- await spawnRunnerPlan(this.buildAddonGroupPlan(allIds.filter((id) => !isolatedBuiltins.has(id))));
405134
+ const { infra: isolatedInfraIds, consumers: isolatedConsumerIds } = (0, system_1.partitionIsolatedBuiltinIds)(isolatedBuiltinIds, (id) => {
405135
+ const entry = this.addonEntries.get(id);
405136
+ return entry?.declaredCapabilities ?? [];
405137
+ });
404552
405138
  const isCoreBuiltin = (id) => this.addonEntries.get(id)?.packageName === "@camstack/system" && !isolatedBuiltins.has(id);
404553
- for (const infra of system_1.INFRA_CAPABILITIES) {
404554
- const addonId = this.findAddonForCapability(infra.name, allIds);
404555
- if (addonId) {
404556
- const entry = this.addonEntries.get(addonId);
404557
- if (!entry || entry.initialized || !isCoreBuiltin(addonId))
404558
- continue;
405139
+ await (0, system_1.runHubAddonBoot)({
405140
+ spawnIsolatedInfra: async () => {
405141
+ if (isolatedInfraIds.length === 0)
405142
+ return;
405143
+ this.logger.info("Spawning isolated infrastructure builtins", {
405144
+ meta: { addonIds: isolatedInfraIds }
405145
+ });
405146
+ await spawnRunnerPlan(this.buildAddonGroupPlan(isolatedInfraIds));
405147
+ },
405148
+ waitForDataStoreProvider: async () => {
405149
+ if (isolatedInfraIds.length === 0)
405150
+ return;
404559
405151
  try {
404560
- await this.initializeAddon(addonId);
404561
- this.wireCapabilities(addonId);
404562
- } catch (error) {
404563
- const msg = (0, types_1.errMsg)(error);
404564
- this.emitAddonLifecycleEvent("addon.error", addonId, {
404565
- error: msg,
404566
- phase: "init"
405152
+ await (0, system_1.waitUntilReady)(() => this.capabilityRegistry.getCollection("data-store-provider").length > 0, {
405153
+ timeoutMs: 3e4,
405154
+ intervalMs: 50,
405155
+ what: "isolated data-store-provider engine"
404567
405156
  });
404568
- if (infra.required) {
404569
- throw new Error(`Required infrastructure addon "${addonId}" failed: ${msg}`, {
404570
- cause: error
404571
- });
404572
- }
404573
- this.logger.warn("Optional infra addon failed -- continuing", {
404574
- tags: { addonId },
404575
- meta: { error: msg }
405157
+ } catch (err) {
405158
+ this.logger.error("Isolated data-store-provider engine did not register in time", {
405159
+ meta: { error: (0, types_1.errMsg)(err), addonIds: isolatedInfraIds }
404576
405160
  });
404577
405161
  }
404578
- } else if (infra.required) {
404579
- throw new Error(`No addon provides required infrastructure capability "${infra.name}"`);
404580
- }
404581
- }
404582
- const bootOrder = this.capabilityRegistry.getBootOrder();
404583
- const infraNames = new Set(system_1.INFRA_CAPABILITIES.map((c) => c.name));
404584
- for (const capName of bootOrder) {
404585
- if (infraNames.has(capName))
404586
- continue;
404587
- for (const id of allIds) {
404588
- const entry = this.addonEntries.get(id);
404589
- if (!entry || entry.initialized || !isCoreBuiltin(id))
404590
- continue;
404591
- const provides = this.getAddonCapabilities(entry.addon);
404592
- if (!provides.some((c) => c.name === capName))
404593
- continue;
404594
- try {
404595
- await this.initializeAddon(id);
404596
- this.wireCapabilities(id);
404597
- } catch (error) {
404598
- const msg = (0, types_1.errMsg)(error);
404599
- this.emitAddonLifecycleEvent("addon.error", id, {
404600
- error: msg,
404601
- phase: "init"
404602
- });
404603
- this.logger.error("Core builtin failed to initialize -- skipping", {
404604
- tags: { addonId: id },
404605
- meta: { error: msg }
404606
- });
405162
+ },
405163
+ bootInProcessInfra: async () => {
405164
+ for (const infra of system_1.INFRA_CAPABILITIES) {
405165
+ const addonId = this.findAddonForCapability(infra.name, allIds);
405166
+ if (addonId) {
405167
+ const entry = this.addonEntries.get(addonId);
405168
+ if (!entry || entry.initialized || !isCoreBuiltin(addonId))
405169
+ continue;
405170
+ try {
405171
+ await this.initializeAddon(addonId);
405172
+ this.wireCapabilities(addonId);
405173
+ } catch (error) {
405174
+ const msg = (0, types_1.errMsg)(error);
405175
+ this.emitAddonLifecycleEvent("addon.error", addonId, {
405176
+ error: msg,
405177
+ phase: "init"
405178
+ });
405179
+ if (infra.required) {
405180
+ throw new Error(`Required infrastructure addon "${addonId}" failed: ${msg}`, {
405181
+ cause: error
405182
+ });
405183
+ }
405184
+ this.logger.warn("Optional infra addon failed -- continuing", {
405185
+ tags: { addonId },
405186
+ meta: { error: msg }
405187
+ });
405188
+ }
405189
+ } else if (infra.required) {
405190
+ throw new Error(`No addon provides required infrastructure capability "${infra.name}"`);
405191
+ }
404607
405192
  }
404608
- }
404609
- }
404610
- for (const id of allIds) {
404611
- const entry = this.addonEntries.get(id);
404612
- if (entry && !entry.initialized && isCoreBuiltin(id)) {
404613
- try {
404614
- await this.initializeAddon(id);
404615
- this.wireCapabilities(id);
404616
- } catch (error) {
404617
- const msg = (0, types_1.errMsg)(error);
404618
- this.emitAddonLifecycleEvent("addon.error", id, {
404619
- error: msg,
404620
- phase: "init"
404621
- });
404622
- this.logger.error("Core builtin failed to initialize -- skipping", {
404623
- tags: { addonId: id },
404624
- meta: { error: msg }
404625
- });
405193
+ },
405194
+ spawnForkedAddons: async () => {
405195
+ await spawnRunnerPlan(this.buildAddonGroupPlan(allIds.filter((id) => !isolatedBuiltins.has(id))));
405196
+ },
405197
+ bootInProcessConsumers: async () => {
405198
+ const bootOrder = this.capabilityRegistry.getBootOrder();
405199
+ const infraNames = new Set(system_1.INFRA_CAPABILITIES.map((c) => c.name));
405200
+ for (const capName of bootOrder) {
405201
+ if (infraNames.has(capName))
405202
+ continue;
405203
+ for (const id of allIds) {
405204
+ const entry = this.addonEntries.get(id);
405205
+ if (!entry || entry.initialized || !isCoreBuiltin(id))
405206
+ continue;
405207
+ const provides = this.getAddonCapabilities(entry.addon);
405208
+ if (!provides.some((c) => c.name === capName))
405209
+ continue;
405210
+ try {
405211
+ await this.initializeAddon(id);
405212
+ this.wireCapabilities(id);
405213
+ } catch (error) {
405214
+ const msg = (0, types_1.errMsg)(error);
405215
+ this.emitAddonLifecycleEvent("addon.error", id, {
405216
+ error: msg,
405217
+ phase: "init"
405218
+ });
405219
+ this.logger.error("Core builtin failed to initialize -- skipping", {
405220
+ tags: { addonId: id },
405221
+ meta: { error: msg }
405222
+ });
405223
+ }
405224
+ }
405225
+ }
405226
+ for (const id of allIds) {
405227
+ const entry = this.addonEntries.get(id);
405228
+ if (entry && !entry.initialized && isCoreBuiltin(id)) {
405229
+ try {
405230
+ await this.initializeAddon(id);
405231
+ this.wireCapabilities(id);
405232
+ } catch (error) {
405233
+ const msg = (0, types_1.errMsg)(error);
405234
+ this.emitAddonLifecycleEvent("addon.error", id, {
405235
+ error: msg,
405236
+ phase: "init"
405237
+ });
405238
+ this.logger.error("Core builtin failed to initialize -- skipping", {
405239
+ tags: { addonId: id },
405240
+ meta: { error: msg }
405241
+ });
405242
+ }
405243
+ }
404626
405244
  }
405245
+ },
405246
+ spawnIsolatedConsumers: async () => {
405247
+ if (isolatedConsumerIds.length === 0)
405248
+ return;
405249
+ this.logger.info("Spawning isolated system builtins", {
405250
+ meta: { addonIds: isolatedConsumerIds }
405251
+ });
405252
+ await spawnRunnerPlan(this.buildAddonGroupPlan(isolatedConsumerIds));
404627
405253
  }
404628
- }
404629
- if (isolatedBuiltinIds.length > 0) {
404630
- this.logger.info("Spawning isolated system builtins", {
404631
- meta: { addonIds: isolatedBuiltinIds }
404632
- });
404633
- await spawnRunnerPlan(this.buildAddonGroupPlan(isolatedBuiltinIds));
404634
- }
405254
+ });
404635
405255
  const initializedIds = [...this.addonEntries.entries()].filter(([, e]) => e.initialized).map(([id]) => id);
404636
405256
  this.logger.info("Addons initialized", {
404637
405257
  meta: { initializedCount: initializedIds.length, totalCount: this.addonEntries.size }
@@ -405894,12 +406514,13 @@ var require_addon_registry_service = __commonJS({
405894
406514
  if (syncStore) {
405895
406515
  this.configService.setSettingsStore(syncStore);
405896
406516
  } else {
405897
- this.logger.error("settings-store provider does not implement the sync ConfigManager surface and no data-store-provider engine does either \u2014 every addon store reads empty, devices will NOT be restored", { meta: { addonId, phase: "v2" } });
406517
+ this.logger.info("settings-store engine is isolated \u2014 ConfigManager uses the async door, not a sync ISettingsStore", { meta: { addonId, phase: "v2" } });
405898
406518
  }
405899
406519
  this.storageService.setSettingsBackend(provider);
405900
406520
  const store = this.capabilityRegistry.getProviderByAddon("settings-store", addonId);
405901
406521
  if (!store)
405902
406522
  return;
406523
+ this.configService.setSettingsDoor(store);
405903
406524
  this.integrationRegistry = new system_1.IntegrationRegistry(store);
405904
406525
  void this.integrationRegistry.initialize().then(() => {
405905
406526
  this.logger.info("IntegrationRegistry initialized", { meta: { phase: "v2" } });
@@ -408373,15 +408994,15 @@ var require_moleculer_service = __commonJS({
408373
408994
  "../../server/backend/dist/core/moleculer/moleculer.service.js"(exports) {
408374
408995
  "use strict";
408375
408996
  Object.defineProperty(exports, "__esModule", { value: true });
408376
- exports.MoleculerService = void 0;
408997
+ exports.ChildManifestGate = exports.CHILD_MANIFEST_SKIP_SAMPLE = exports.MoleculerService = void 0;
408377
408998
  exports.childOwnerToken = childOwnerToken;
408378
408999
  exports.buildChildUdsManifest = buildChildUdsManifest;
408379
409000
  var node_crypto_1 = __require("crypto");
408380
409001
  var system_1 = require_dist3();
408381
409002
  var types_1 = require_dist9();
408382
- var agent_readiness_pull_js_1 = require_agent_readiness_pull();
408383
409003
  var cap_router_runtime_js_1 = require_cap_router_runtime();
408384
409004
  var core_cap_bridge_js_1 = require_core_cap_bridge();
409005
+ var agent_readiness_pull_js_1 = require_agent_readiness_pull();
408385
409006
  var cap_call_fn_js_1 = require_cap_call_fn();
408386
409007
  var cap_route_authority_js_1 = require_cap_route_authority();
408387
409008
  var MoleculerService = class _MoleculerService {
@@ -408416,6 +409037,12 @@ var require_moleculer_service = __commonJS({
408416
409037
  * See `docs/decisions/adr-0188-an-unregister-carries-proof-of-ownership.md`.
408417
409038
  */
408418
409039
  nodeOwners = /* @__PURE__ */ new Map();
409040
+ /**
409041
+ * Skips the manifest rebuild for a child re-register that carries nothing new
409042
+ * — 246 of them in 30 minutes with zero respawns, measured live 2026-08-25.
409043
+ * See {@link ChildManifestGate}.
409044
+ */
409045
+ childManifestGate = new ChildManifestGate();
408419
409046
  /**
408420
409047
  * Fixed-period agent-readiness snapshot sweep (D8 reconcile) — repairs
408421
409048
  * agent-origin readiness deltas lost while the agent stayed connected.
@@ -408737,6 +409364,19 @@ var require_moleculer_service = __commonJS({
408737
409364
  const hubNodeId = this.brokerSafe.nodeID;
408738
409365
  const childNodeId = `${hubNodeId}/${child.childId}`;
408739
409366
  const params = buildChildUdsManifest(childNodeId, child.childId, child.caps);
409367
+ if (!this.childManifestGate.shouldApply(child.childId, child.incarnation, params)) {
409368
+ if (this.childManifestGate.sampleDue()) {
409369
+ logger.debug("UDS child re-registered with an unchanged manifest \u2014 skipped", {
409370
+ meta: {
409371
+ nodeId: childNodeId,
409372
+ incarnation: child.incarnation,
409373
+ skipped: this.childManifestGate.skippedSince(),
409374
+ sampleEvery: exports.CHILD_MANIFEST_SKIP_SAMPLE
409375
+ }
409376
+ });
409377
+ }
409378
+ return;
409379
+ }
408740
409380
  this.onRegisterNode(params, childOwnerToken(childNodeId, child.incarnation));
408741
409381
  logger.info("UDS child registered \u2014 manifest applied", {
408742
409382
  meta: { nodeId: childNodeId, incarnation: child.incarnation }
@@ -408745,6 +409385,7 @@ var require_moleculer_service = __commonJS({
408745
409385
  registry.onChildGone((childId, incarnation) => {
408746
409386
  const hubNodeId = this.brokerSafe.nodeID;
408747
409387
  const childNodeId = `${hubNodeId}/${childId}`;
409388
+ this.childManifestGate.forget(childId);
408748
409389
  logger.info("UDS child gone \u2014 removing from registry", {
408749
409390
  meta: { childId, incarnation }
408750
409391
  });
@@ -408979,12 +409620,13 @@ var require_moleculer_service = __commonJS({
408979
409620
  if (!registry)
408980
409621
  return;
408981
409622
  const registryKeyFor = (addonId) => isLocalChild ? addonId : `${addonId}@${nodeId}`;
409623
+ const skipInfraFromRemote = (capName) => (0, system_1.isInfraCapability)(capName) && !isLocalChild;
408982
409624
  const appliedKeys = (manifest) => {
408983
409625
  const keys = /* @__PURE__ */ new Map();
408984
409626
  for (const addon of manifest) {
408985
409627
  const registryKey = registryKeyFor(addon.addonId);
408986
409628
  for (const capName of addon.capabilities) {
408987
- if ((0, system_1.isInfraCapability)(capName))
409629
+ if (skipInfraFromRemote(capName))
408988
409630
  continue;
408989
409631
  const capDef = registry.getDefinition(capName);
408990
409632
  if (!capDef)
@@ -409275,6 +409917,48 @@ var require_moleculer_service = __commonJS({
409275
409917
  function childOwnerToken(childNodeId, incarnation) {
409276
409918
  return `${childNodeId}#${incarnation}`;
409277
409919
  }
409920
+ exports.CHILD_MANIFEST_SKIP_SAMPLE = 25;
409921
+ function childManifestFingerprint(params) {
409922
+ const addons = params.addons.map((addon) => `${addon.addonId}:${[...addon.capabilities].sort().join(",")}`).sort();
409923
+ return `${params.nodeId}|${addons.join("|")}`;
409924
+ }
409925
+ var ChildManifestGate = class {
409926
+ applied = /* @__PURE__ */ new Map();
409927
+ skipped = 0;
409928
+ sinceSample = 0;
409929
+ /** True when this registration carries something the registry does not have. */
409930
+ shouldApply(childId, incarnation, params) {
409931
+ const fingerprint = childManifestFingerprint(params);
409932
+ const previous = this.applied.get(childId);
409933
+ if (previous !== void 0 && previous.incarnation === incarnation) {
409934
+ if (previous.fingerprint === fingerprint) {
409935
+ this.skipped += 1;
409936
+ this.sinceSample += 1;
409937
+ return false;
409938
+ }
409939
+ }
409940
+ this.applied.set(childId, { incarnation, fingerprint });
409941
+ return true;
409942
+ }
409943
+ /** Drop a child's record — its next register is a first register again. */
409944
+ forget(childId) {
409945
+ this.applied.delete(childId);
409946
+ }
409947
+ /** How many have been skipped since the last call. Reading RESETS it. */
409948
+ skippedSince() {
409949
+ const n = this.skipped;
409950
+ this.skipped = 0;
409951
+ return n;
409952
+ }
409953
+ /** True once every {@link CHILD_MANIFEST_SKIP_SAMPLE} skips. */
409954
+ sampleDue() {
409955
+ if (this.sinceSample < exports.CHILD_MANIFEST_SKIP_SAMPLE)
409956
+ return false;
409957
+ this.sinceSample = 0;
409958
+ return true;
409959
+ }
409960
+ };
409961
+ exports.ChildManifestGate = ChildManifestGate;
409278
409962
  function buildChildUdsManifest(nodeId, childId, caps) {
409279
409963
  const capsByAddon = /* @__PURE__ */ new Map();
409280
409964
  for (const cap of caps) {