@camstack/addon-export-hap 1.2.38 → 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.
@@ -5874,6 +5874,13 @@ var BaseAddon = class {
5874
5874
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5875
5875
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5876
5876
  _registeredCapNames = [];
5877
+ /**
5878
+ * True only after `readAddonStore` actually answered. Constructor
5879
+ * defaults look like stored config when the store is down — a forked
5880
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5881
+ * mode, 2026-08-25) is not "the operator chose this".
5882
+ */
5883
+ settingsStoreReady = false;
5877
5884
  /** Default config values. Provided via constructor. */
5878
5885
  defaults;
5879
5886
  constructor(defaults) {
@@ -6274,7 +6281,9 @@ var BaseAddon = class {
6274
6281
  ];
6275
6282
  let lastErr;
6276
6283
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6277
- return await settings.readAddonStore() ?? {};
6284
+ const stored = await settings.readAddonStore() ?? {};
6285
+ this.settingsStoreReady = true;
6286
+ return stored;
6278
6287
  } catch (err) {
6279
6288
  lastErr = err;
6280
6289
  const msg = err instanceof Error ? err.message : String(err);
@@ -6282,6 +6291,7 @@ var BaseAddon = class {
6282
6291
  if (attempt === delaysMs.length) break;
6283
6292
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6284
6293
  }
6294
+ this.settingsStoreReady = false;
6285
6295
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6286
6296
  return {};
6287
6297
  }
@@ -6752,7 +6762,7 @@ function method(input, output, options) {
6752
6762
  input,
6753
6763
  output,
6754
6764
  kind: options?.kind ?? "query",
6755
- auth: options?.auth ?? "protected",
6765
+ ...options?.auth !== void 0 ? { auth: options.auth } : {},
6756
6766
  ...options?.access !== void 0 ? { access: options.access } : {},
6757
6767
  ...options?.caller !== void 0 ? { caller: options.caller } : {},
6758
6768
  timeoutMs: options?.timeoutMs
@@ -6772,7 +6782,7 @@ function systemMethod(input, output, options) {
6772
6782
  }
6773
6783
  var StaticDirOutputSchema$1 = object({ staticDir: string() });
6774
6784
  var VersionOutputSchema$1 = object({ version: string() });
6775
- method(_void(), StaticDirOutputSchema$1), method(_void(), VersionOutputSchema$1);
6785
+ method(_void(), StaticDirOutputSchema$1, { auth: "admin" }), method(_void(), VersionOutputSchema$1, { auth: "admin" });
6776
6786
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
6777
6787
  DeviceType["Camera"] = "camera";
6778
6788
  DeviceType["Hub"] = "hub";
@@ -7093,7 +7103,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
7093
7103
  }({});
7094
7104
  var StaticDirOutputSchema = object({ staticDir: string() });
7095
7105
  var VersionOutputSchema = object({ version: string() });
7096
- method(_void(), StaticDirOutputSchema), method(_void(), VersionOutputSchema);
7106
+ method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
7097
7107
  /**
7098
7108
  * device-ops — device-scoped cap that unifies the per-IDevice operations
7099
7109
  * previously routed through the `.device-ops` Moleculer bridge service.
@@ -8245,24 +8255,6 @@ var RecordingRetentionSchema = object({
8245
8255
  maxSizeGb: number().min(0).optional()
8246
8256
  });
8247
8257
  /**
8248
- * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
8249
- * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
8250
- * previews at. Five graduated steps; absent on a config = `standard` (the
8251
- * shipped default, matching `sheet-geometry`/`sheet-composer`).
8252
- *
8253
- * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
8254
- * Each window's index sidecar carries its own tile dims, so a camera whose
8255
- * preset changed over time renders every historical window at the dims it was
8256
- * written with.
8257
- */
8258
- var ScrubThumbnailPresetSchema = _enum([
8259
- "minimal",
8260
- "low",
8261
- "standard",
8262
- "high",
8263
- "max"
8264
- ]);
8265
- /**
8266
8258
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
8267
8259
  *
8268
8260
  * `bands` is the ONLY authored recording intent: what to record, when, and on
@@ -8270,7 +8262,11 @@ var ScrubThumbnailPresetSchema = _enum([
8270
8262
  * other field is a storage knob (profiles, segment length, retention, scrub).
8271
8263
  *
8272
8264
  * STRICT on purpose: the legacy authoring surface (`schedule`/`schedules`/
8273
- * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30.
8265
+ * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30,
8266
+ * and `scrubThumbnails` — a five-step fidelity knob for a sprite tier that was
8267
+ * deleted on 2026-07-24 and had ZERO consumers in the recorder — on 2026-08-25
8268
+ * (D62: a switch that writes a store nobody reads is worse than no switch).
8269
+ * Stored rows keep loading: the READ schema is `.strip()` (config-store.ts).
8274
8270
  * A stale caller must fail loudly — silently stripping its legacy intent would
8275
8271
  * persist a band-less config, i.e. silently stop recording the camera.
8276
8272
  */
@@ -8293,14 +8289,7 @@ var RecordingConfigSchema = object({
8293
8289
  * "off" is the absence of a covering band, never a band value.
8294
8290
  */
8295
8291
  bands: array(RecordingBandSchema).default([]),
8296
- retention: RecordingRetentionSchema.optional(),
8297
- /**
8298
- * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
8299
- * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
8300
- * windows only — existing sheets are immutable, and each window's index
8301
- * carries its own tile dims so mixed-preset history renders correctly.
8302
- */
8303
- scrubThumbnails: ScrubThumbnailPresetSchema.optional()
8292
+ retention: RecordingRetentionSchema.optional()
8304
8293
  }).strict();
8305
8294
  /**
8306
8295
  * Entity-relocation job state (storage entity-routing spec, Phase 4).
@@ -8376,10 +8365,11 @@ var RelocateFootageInputSchema = object({
8376
8365
  * `RecordingConfig.enabled` or camera wrapper bindings. */
8377
8366
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
8378
8367
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
8379
- var StorageMigrationMediaMoveInputSchema = object({
8368
+ var RelocateMediaInputSchema = object({
8380
8369
  toLocationId: string(),
8381
8370
  throttleMbps: number().min(1).max(1e3).optional()
8382
- }).extend({ leaseId: string().min(1) });
8371
+ });
8372
+ var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
8383
8373
  /** The independently selectable logical storage classes. `recordings`
8384
8374
  * encompasses the high and mid segment profiles; `recordingsLow` is low
8385
8375
  * segments; `eventMedia` is post-analysis blobs. */
@@ -8728,7 +8718,26 @@ var LabelDefinitionSchema = object({
8728
8718
  description: string().optional(),
8729
8719
  icon: string().optional()
8730
8720
  });
8731
- var ClassMapDefinitionSchema = object({
8721
+ /**
8722
+ * Wire schema for a per-model CATALOG classMap override
8723
+ * (`ModelCatalogEntry.classMap` / `ModelConvertMetadata.classMap`) —
8724
+ * restricted to {@link CLASS_MAP_MACRO_TARGETS}, the only macros the
8725
+ * detection pipeline executor actually routes.
8726
+ *
8727
+ * This is deliberately a DIFFERENT, narrower shape than the general-purpose
8728
+ * `ClassMapDefinition` interface above (e.g. `IDetectionAddon.getClassMap()`
8729
+ * and the audio `YAMNET_TO_MACRO` catalog both use macro targets outside this
8730
+ * enum) — the two used to share the name `ClassMapDefinition`/
8731
+ * `ClassMapDefinitionSchema`, which made the schema-type-twin guard
8732
+ * (`scripts/check-schema-type-twins.ts`) flag them as a duplicated shape. They
8733
+ * are not: it is two different concepts colliding on a name. Keep this type
8734
+ * under its own name rather than reusing `ClassMapDefinition` — reusing it
8735
+ * would either narrow every `ClassMapDefinition` consumer to the four
8736
+ * detection macros (breaking `YAMNET_TO_MACRO`) or drop the validation this
8737
+ * schema exists for (see the "rejects a classMap whose target is not a
8738
+ * detection macro" test in `model-catalog-schema.test.ts`).
8739
+ */
8740
+ var DetectionCatalogClassMapSchema = object({
8732
8741
  mapping: record(string(), _enum([
8733
8742
  "person",
8734
8743
  "vehicle",
@@ -8820,6 +8829,12 @@ var ModelVariantGroupSchema = object({
8820
8829
  */
8821
8830
  resolution: number().int().positive().optional()
8822
8831
  });
8832
+ var ModelProviderIdSchema = _enum([
8833
+ "camstack",
8834
+ "frigate",
8835
+ "scrypted",
8836
+ "custom"
8837
+ ]);
8823
8838
  var ModelCatalogEntrySchema = object({
8824
8839
  id: string(),
8825
8840
  name: string(),
@@ -8917,11 +8932,17 @@ var ModelCatalogEntrySchema = object({
8917
8932
  */
8918
8933
  group: ModelVariantGroupSchema.optional(),
8919
8934
  /**
8935
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8936
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8937
+ * persisted before this field existed (`inferModelProvider` fills those).
8938
+ */
8939
+ provider: ModelProviderIdSchema.optional(),
8940
+ /**
8920
8941
  * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8921
8942
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8922
8943
  * labels already ARE the CamStack macros (Scrypted identity map).
8923
8944
  */
8924
- classMap: ClassMapDefinitionSchema.optional()
8945
+ classMap: DetectionCatalogClassMapSchema.optional()
8925
8946
  });
8926
8947
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8927
8948
  format: literal("openvino"),
@@ -8951,7 +8972,7 @@ var ModelConvertMetadataSchema = object({
8951
8972
  "segmentation"
8952
8973
  ]),
8953
8974
  faceAlignment: boolean().optional(),
8954
- classMap: ClassMapDefinitionSchema.optional()
8975
+ classMap: DetectionCatalogClassMapSchema.optional()
8955
8976
  });
8956
8977
  var ConvertResultSchema = object({
8957
8978
  entry: ModelCatalogEntrySchema,
@@ -9814,7 +9835,7 @@ var AddonPageDeclarationSchema = object({
9814
9835
  /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
9815
9836
  sectionLabel: string().optional()
9816
9837
  });
9817
- method(_void(), array(AddonPageDeclarationSchema).readonly());
9838
+ method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
9818
9839
  var AddonHttpRouteSchema = object({
9819
9840
  method: _enum([
9820
9841
  "GET",
@@ -10049,7 +10070,7 @@ var WidgetMetadataSchema = object({
10049
10070
  defaultColumns: number().int().min(1).max(12).default(6),
10050
10071
  defaultRows: number().int().min(1).max(12).default(1)
10051
10072
  });
10052
- method(_void(), array(WidgetMetadataSchema).readonly());
10073
+ method(_void(), array(WidgetMetadataSchema).readonly(), { auth: "admin" });
10053
10074
  /**
10054
10075
  * `addon-widgets` — system-scoped singleton aggregator cap. Public-facing
10055
10076
  * surface that admin-ui consumes through `useAddonWidgetsListWidgets()`.
@@ -11571,7 +11592,7 @@ var CustomModelDescriptorSchema = object({
11571
11592
  stepId: string(),
11572
11593
  entry: ModelCatalogEntrySchema
11573
11594
  });
11574
- method(_void(), array(CustomModelDescriptorSchema).readonly());
11595
+ method(_void(), array(CustomModelDescriptorSchema).readonly(), { auth: "admin" });
11575
11596
  /**
11576
11597
  * Query filter for settings-store collections.
11577
11598
  */
@@ -11658,7 +11679,8 @@ method(object({
11658
11679
  }), _void(), { kind: "mutation" }), method(object({
11659
11680
  namespace: string().optional(),
11660
11681
  collection: string(),
11661
- filter: QueryFilterSchema.optional()
11682
+ filter: QueryFilterSchema.optional(),
11683
+ columns: array(string()).readonly().optional()
11662
11684
  }), array(SettingsRecordSchema).readonly()), method(object({
11663
11685
  namespace: string().optional(),
11664
11686
  collection: string(),
@@ -11721,46 +11743,87 @@ var EngineInfoSchema = object({
11721
11743
  kind: _enum(["relational", "vector"]),
11722
11744
  displayName: string()
11723
11745
  });
11724
- method(_void(), EngineInfoSchema), method(object({
11746
+ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
11725
11747
  namespace: string().optional(),
11726
11748
  collection: string(),
11727
11749
  key: string()
11728
- }), unknown()), method(object({
11750
+ }), unknown(), { auth: "admin" }), method(object({
11729
11751
  namespace: string().optional(),
11730
11752
  collection: string(),
11731
11753
  key: string(),
11732
11754
  value: unknown()
11733
- }), _void(), { kind: "mutation" }), method(object({
11755
+ }), _void(), {
11756
+ kind: "mutation",
11757
+ auth: "admin"
11758
+ }), method(object({
11734
11759
  namespace: string().optional(),
11735
11760
  collection: string(),
11736
- filter: QueryFilterSchema.optional()
11737
- }), array(SettingsRecordSchema).readonly()), method(object({
11761
+ filter: QueryFilterSchema.optional(),
11762
+ /**
11763
+ * SQL-level column projection — MUST mirror `settings-store.query`.
11764
+ *
11765
+ * ⚠ An earlier version of this comment blamed Zod stripping, and that
11766
+ * was wrong — corrected 2026-08-26 after the hop map
11767
+ * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
11768
+ * There is **no Zod parse at all** between the door and the engine: the
11769
+ * dispatcher forwards the payload verbatim and UDS carries it whole. A
11770
+ * field declared here reaches `SqliteSettingsBackend` either way.
11771
+ *
11772
+ * What actually lost `columns` was the THIRD declaration of this shape:
11773
+ * `SettingsQueryInput` in `interfaces/storage.ts`, a hand-written TS
11774
+ * interface the engine destructures from. The field existed on both
11775
+ * schemas and the engine still never read it, because nothing checks a
11776
+ * registered provider against `InferProvider<cap>` —
11777
+ * `ProviderRegistration.provider` is typed `object`.
11778
+ *
11779
+ * It is declared here anyway, and must stay in step with
11780
+ * `settings-store.query`: a caller reading only the cap definitions has
11781
+ * to be able to see that this call carries a projection.
11782
+ * `data-door-schema-parity.spec.ts` keeps the two aligned.
11783
+ */
11784
+ columns: array(string()).readonly().optional()
11785
+ }), array(SettingsRecordSchema).readonly(), { auth: "admin" }), method(object({
11738
11786
  namespace: string().optional(),
11739
11787
  collection: string(),
11740
11788
  record: SettingsRecordSchema
11741
- }), _void(), { kind: "mutation" }), method(object({
11789
+ }), _void(), {
11790
+ kind: "mutation",
11791
+ auth: "admin"
11792
+ }), method(object({
11742
11793
  namespace: string().optional(),
11743
11794
  collection: string(),
11744
11795
  id: string(),
11745
11796
  data: record(string(), unknown())
11746
- }), _void(), { kind: "mutation" }), method(object({
11797
+ }), _void(), {
11798
+ kind: "mutation",
11799
+ auth: "admin"
11800
+ }), method(object({
11747
11801
  namespace: string().optional(),
11748
11802
  collection: string(),
11749
11803
  key: string()
11750
- }), _void(), { kind: "mutation" }), method(object({
11804
+ }), _void(), {
11805
+ kind: "mutation",
11806
+ auth: "admin"
11807
+ }), method(object({
11751
11808
  namespace: string().optional(),
11752
11809
  collection: string(),
11753
11810
  filter: MutationFilterSchema
11754
- }), object({ deleted: number().int() }), { kind: "mutation" }), method(object({
11811
+ }), object({ deleted: number().int() }), {
11812
+ kind: "mutation",
11813
+ auth: "admin"
11814
+ }), method(object({
11755
11815
  namespace: string().optional(),
11756
11816
  collection: string(),
11757
11817
  filter: MutationFilterSchema,
11758
11818
  data: record(string(), unknown())
11759
- }), object({ updated: number().int() }), { kind: "mutation" }), method(object({
11819
+ }), object({ updated: number().int() }), {
11820
+ kind: "mutation",
11821
+ auth: "admin"
11822
+ }), method(object({
11760
11823
  namespace: string().optional(),
11761
11824
  collection: string(),
11762
11825
  filter: QueryFilterSchema.optional()
11763
- }), number()), method(object({
11826
+ }), number(), { auth: "admin" }), method(object({
11764
11827
  namespace: string().optional(),
11765
11828
  collection: string(),
11766
11829
  field: string(),
@@ -11770,15 +11833,18 @@ method(_void(), EngineInfoSchema), method(object({
11770
11833
  }), array(object({
11771
11834
  bucket: number().int(),
11772
11835
  count: number().int()
11773
- })).readonly()), method(object({
11836
+ })).readonly(), { auth: "admin" }), method(object({
11774
11837
  namespace: string().optional(),
11775
11838
  collection: string()
11776
- }), boolean()), method(object({
11839
+ }), boolean(), { auth: "admin" }), method(object({
11777
11840
  namespace: string().optional(),
11778
11841
  collection: string(),
11779
11842
  columns: array(CollectionColumnSchema).readonly(),
11780
11843
  indexes: array(CollectionIndexSchema).readonly().optional()
11781
- }), _void(), { kind: "mutation" });
11844
+ }), _void(), {
11845
+ kind: "mutation",
11846
+ auth: "admin"
11847
+ });
11782
11848
  /**
11783
11849
  * Stable UI option list for the `hwaccel` setting. Decoder addons
11784
11850
  * reuse this for `globalSettingsSchema()` so the dropdown is
@@ -12525,6 +12591,27 @@ var LinkedDeviceSchema = object({
12525
12591
  features: array(string()),
12526
12592
  producesTrackedEvents: boolean().optional()
12527
12593
  });
12594
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
12595
+ * The batch answer needs the tag; the single-device answer already has it
12596
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
12597
+ var LinkedDevicesForDeviceSchema = object({
12598
+ deviceId: number(),
12599
+ mode: LinkedDevicesModeSchema,
12600
+ devices: array(LinkedDeviceSchema)
12601
+ });
12602
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
12603
+ * `getAllBindings` all answer in. Declared once: three copies of the same
12604
+ * object literal is exactly how the three drift apart. */
12605
+ var DeviceBindingsForDeviceSchema = object({
12606
+ deviceId: number(),
12607
+ entries: array(object({
12608
+ capName: string(),
12609
+ kind: _enum(["native", "wrapped"]),
12610
+ providerAddonId: string(),
12611
+ providerNodeId: string(),
12612
+ nativeAddonId: string()
12613
+ }))
12614
+ });
12528
12615
  var SavedDeviceRowSchema = object({
12529
12616
  /** Numeric id reserved at allocateDeviceId time. */
12530
12617
  id: number(),
@@ -12750,11 +12837,25 @@ method(object({
12750
12837
  projection: _enum(["full", "slim"]).optional(),
12751
12838
  /** Return only camera devices. Filtering server-side instead of
12752
12839
  * shipping 293 rows to find 12. */
12753
- isCamera: boolean().optional()
12840
+ isCamera: boolean().optional(),
12841
+ /**
12842
+ * Return only these device ids. For the caller that already KNOWS the
12843
+ * handful it wants and needs a field the id-bearing answer does not
12844
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12845
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12846
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12847
+ * refetches on the reconcile interval, on a phone.
12848
+ *
12849
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12850
+ * keys rather than rejecting them (verified against the live hub
12851
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12852
+ * it answers today and the caller filters as it already does.
12853
+ */
12854
+ deviceIds: array(number()).optional()
12754
12855
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12755
12856
  mode: LinkedDevicesModeSchema,
12756
12857
  devices: array(LinkedDeviceSchema)
12757
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12858
+ })), method(object({ deviceIds: array(number()) }), array(LinkedDevicesForDeviceSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12758
12859
  deviceId: number(),
12759
12860
  values: record(string(), unknown())
12760
12861
  }), object({ success: literal(true) }), {
@@ -12781,25 +12882,7 @@ method(object({
12781
12882
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
12782
12883
  kind: "mutation",
12783
12884
  auth: "admin"
12784
- }), method(object({ deviceId: number() }), object({
12785
- deviceId: number(),
12786
- entries: array(object({
12787
- capName: string(),
12788
- kind: _enum(["native", "wrapped"]),
12789
- providerAddonId: string(),
12790
- providerNodeId: string(),
12791
- nativeAddonId: string()
12792
- }))
12793
- })), method(object({}), array(object({
12794
- deviceId: number(),
12795
- entries: array(object({
12796
- capName: string(),
12797
- kind: _enum(["native", "wrapped"]),
12798
- providerAddonId: string(),
12799
- providerNodeId: string(),
12800
- nativeAddonId: string()
12801
- }))
12802
- }))), method(object({
12885
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12803
12886
  deviceId: number(),
12804
12887
  capName: string(),
12805
12888
  wrapperAddonId: string(),
@@ -12970,7 +13053,7 @@ method(object({
12970
13053
  crop: _instanceof(Uint8Array),
12971
13054
  width: number(),
12972
13055
  height: number()
12973
- }), EmbeddingResultSchema), method(object({ text: string() }), EmbeddingResultSchema), method(_void(), EmbeddingInfoSchema);
13056
+ }), EmbeddingResultSchema, { auth: "admin" }), method(object({ text: string() }), EmbeddingResultSchema, { auth: "admin" }), method(_void(), EmbeddingInfoSchema, { auth: "admin" });
12974
13057
  /**
12975
13058
  * filesystem-browse — per-node capability for browsing the node's local
12976
13059
  * filesystem. Reads are unconfined (whole filesystem, from `/` down); WRITES
@@ -13263,19 +13346,22 @@ method(LlmGenerateBaseInputSchema.extend({
13263
13346
  runtime: ManagedRuntimeConfigSchema,
13264
13347
  /** The managed profile's timeout, threaded by the hub provider. */
13265
13348
  timeoutMs: number().int().positive().optional()
13266
- }), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
13349
+ }), LlmGenerateResultSchema, {
13350
+ kind: "mutation",
13351
+ auth: "admin"
13352
+ }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
13267
13353
  kind: "mutation",
13268
13354
  auth: "admin"
13269
13355
  }), method(object({}), _void(), {
13270
13356
  kind: "mutation",
13271
13357
  auth: "admin"
13272
- }), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
13358
+ }), method(object({}), LlmRuntimeStatusSchema, { auth: "admin" }), method(object({ model: ManagedModelRefSchema }), _void(), {
13273
13359
  kind: "mutation",
13274
13360
  auth: "admin"
13275
13361
  }), method(object({ file: string() }), _void(), {
13276
13362
  kind: "mutation",
13277
13363
  auth: "admin"
13278
- }), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
13364
+ }), method(object({}), array(LlmNodeModelSchema), { auth: "admin" }), method(object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" });
13279
13365
  /**
13280
13366
  * `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
13281
13367
  * methods concat-fan across providers; single-row methods route to ONE
@@ -15171,12 +15257,15 @@ var NcOccupancyConditionSchema = object({
15171
15257
  * there is no second switch that can disagree with the first and every rule
15172
15258
  * authored before the decision migrates for free (`audioModeOf`):
15173
15259
  *
15174
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
15175
- * classifier labels with one of them. No window, no percentage:
15176
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
15177
- * `throttle` cooldown is the only brake. The per-label confidence floor is
15178
- * the analyzer's (`classificationMinScore`, per device) a label only
15179
- * reaches this condition if the classifier was already confident enough.
15260
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
15261
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
15262
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
15263
+ * frames is the wrong question for a classifier that labels 1–3 frames
15264
+ * per episode. The count window is the brake that drops a single-frame
15265
+ * false positive; the rule's own `throttle` cooldown is the other. The
15266
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
15267
+ * per device) — a label only reaches this condition if the classifier was
15268
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
15180
15269
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
15181
15270
  * the condition: at least `hitPercent`% of the samples over
15182
15271
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -15203,14 +15292,22 @@ var NcOccupancyConditionSchema = object({
15203
15292
  * an operator who typed `dog` mean the same thing.
15204
15293
  */
15205
15294
  var NcAudioConditionSchema = object({
15206
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
15295
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
15207
15296
  labels: array(string().min(1)).min(1).optional(),
15208
15297
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
15209
15298
  dbThreshold: number().min(-96).max(0).optional(),
15210
15299
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
15211
15300
  hitPercent: number().int().min(1).max(100).default(60),
15212
15301
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
15213
- samplingSeconds: number().int().min(1).max(300).default(10)
15302
+ samplingSeconds: number().int().min(1).max(300).default(10),
15303
+ /**
15304
+ * LABEL MODE: how many labelled frames must land inside
15305
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
15306
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
15307
+ */
15308
+ confirmHits: number().int().min(1).max(20).optional(),
15309
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
15310
+ confirmWindowSec: number().int().min(1).max(60).optional()
15214
15311
  });
15215
15312
  /**
15216
15313
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -16737,7 +16834,7 @@ var OauthIntegrationDescriptorSchema = object({
16737
16834
  */
16738
16835
  refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
16739
16836
  });
16740
- method(_void(), OauthIntegrationDescriptorSchema);
16837
+ method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
16741
16838
  /**
16742
16839
  * pipeline-analytics — device-scoped wrapper cap. Refines raw
16743
16840
  * per-frame detections emitted by the pipeline runner into tracked
@@ -17584,6 +17681,46 @@ var RecentTracksPageSchema = object({
17584
17681
  /** Cursor for the next page, or null when this page is the last. */
17585
17682
  nextCursor: string().nullable()
17586
17683
  });
17684
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
17685
+ var LIST_GROUPS_MAX_LIMIT = 100;
17686
+ var AnalyticsGroupRecordSchema = object({
17687
+ id: string(),
17688
+ deviceId: number().int(),
17689
+ openedAt: number().int(),
17690
+ closedAt: number().int(),
17691
+ timestamp: number().int(),
17692
+ memberCount: number().int(),
17693
+ memberTrackIds: array(string()).readonly(),
17694
+ className: string(),
17695
+ classes: array(string()).readonly(),
17696
+ /** Relative event-media path, or null when the group has no picture yet. */
17697
+ mediaUrl: string().nullable(),
17698
+ singleton: boolean()
17699
+ });
17700
+ var AnalyticsGroupMemberSchema = object({
17701
+ trackId: string(),
17702
+ deviceId: number().int(),
17703
+ className: string(),
17704
+ firstSeen: number().int(),
17705
+ lastSeen: number().int(),
17706
+ mediaUrl: string().nullable()
17707
+ });
17708
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
17709
+ var ListGroupsQueryInput = object({
17710
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
17711
+ deviceIds: array(number()),
17712
+ /** Window lower bound on `closedAt` (inclusive). */
17713
+ since: number().optional(),
17714
+ /** Window upper bound on `openedAt` (inclusive). */
17715
+ until: number().optional(),
17716
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
17717
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
17718
+ cursor: string().optional()
17719
+ });
17720
+ var ListGroupsPageSchema = object({
17721
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
17722
+ nextCursor: string().nullable()
17723
+ });
17587
17724
  var KeyEventQueryInput = object({
17588
17725
  deviceId: number(),
17589
17726
  /** Window lower bound (track firstSeen ≥ since). */
@@ -17659,7 +17796,9 @@ var TrackCascadeCountsSchema = object({
17659
17796
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
17660
17797
  plates: number().int(),
17661
17798
  /** Per-track CLIP search vectors removed (best-effort). */
17662
- embeddings: number().int()
17799
+ embeddings: number().int(),
17800
+ /** Group membership + group rows removed with their last member (best-effort). */
17801
+ groups: number().int()
17663
17802
  });
17664
17803
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
17665
17804
  var DiskReconcileCountsSchema = object({
@@ -17805,7 +17944,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17805
17944
  * stationary registry). Default false: the timeline lists passages,
17806
17945
  * not parking records (operator decision, 2026-08-15). */
17807
17946
  includeStationary: boolean().optional()
17808
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17947
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17948
+ deviceId: number(),
17949
+ groupId: string().min(1)
17950
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17809
17951
  kind: "mutation",
17810
17952
  auth: "admin"
17811
17953
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -17887,6 +18029,15 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17887
18029
  }), method(object({ jobId: string() }), RelocateJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
17888
18030
  kind: "mutation",
17889
18031
  auth: "admin"
18032
+ }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
18033
+ kind: "mutation",
18034
+ auth: "admin"
18035
+ }), method(object({}), array(RelocateJobSchema).readonly(), {
18036
+ kind: "query",
18037
+ auth: "admin"
18038
+ }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
18039
+ kind: "mutation",
18040
+ auth: "admin"
17890
18041
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
17891
18042
  kind: "query",
17892
18043
  auth: "admin"
@@ -18041,6 +18192,11 @@ object({
18041
18192
  "jpeg"
18042
18193
  ])
18043
18194
  });
18195
+ /**
18196
+ * Process-local frame identity. It is serializable so it can ride an in-process
18197
+ * capability call, but `registryId` deliberately prevents resolution in any
18198
+ * other process or execution group.
18199
+ */
18044
18200
  var FrameRefSchema = object({
18045
18201
  registryId: string().min(1),
18046
18202
  id: string().min(1),
@@ -18115,7 +18271,8 @@ var PipelineModelOptionSchema = object({
18115
18271
  sizeMB: number()
18116
18272
  })),
18117
18273
  group: ModelVariantGroupSchema.optional(),
18118
- legacy: boolean().optional()
18274
+ legacy: boolean().optional(),
18275
+ provider: ModelProviderIdSchema.optional()
18119
18276
  });
18120
18277
  var ConfigFieldBridge = custom();
18121
18278
  var PipelineAddonSchemaSchema = object({
@@ -20205,7 +20362,7 @@ method(object({
20205
20362
  * linking rather than produce an eternal token.
20206
20363
  */
20207
20364
  ttlSec: union([number().int().positive(), literal("never")]).optional()
20208
- }), object({ token: string() })), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable());
20365
+ }), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
20209
20366
  var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
20210
20367
  providerId: string().min(1),
20211
20368
  displayName: string().min(1),
@@ -20300,10 +20457,13 @@ var EvictResultSchema = object({
20300
20457
  /** True when the provider has nothing left it is willing to drop on this location. */
20301
20458
  exhausted: boolean()
20302
20459
  });
20303
- method(object({ locationId: string() }), EvictableUsageSchema), method(object({
20460
+ method(object({ locationId: string() }), EvictableUsageSchema, { auth: "admin" }), method(object({
20304
20461
  locationId: string(),
20305
20462
  targetBytes: number().int().positive()
20306
- }), EvictResultSchema, { kind: "mutation" });
20463
+ }), EvictResultSchema, {
20464
+ kind: "mutation",
20465
+ auth: "admin"
20466
+ });
20307
20467
  method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin" }), method(StorageMigrationInputSchema, object({ jobId: string() }), {
20308
20468
  kind: "mutation",
20309
20469
  auth: "admin"
@@ -20363,26 +20523,50 @@ var ReadChunkInputSchema = object({
20363
20523
  length: number()
20364
20524
  });
20365
20525
  var EndDownloadInputSchema = object({ downloadId: string() });
20366
- method(_void(), ProviderInfoSchema), method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }), method(object({
20526
+ method(_void(), ProviderInfoSchema, { auth: "admin" }), method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }), method(object({
20367
20527
  location: StorageLocationSchema,
20368
20528
  relativePath: string()
20369
- }), string()), method(object({
20529
+ }), string(), { auth: "admin" }), method(object({
20370
20530
  location: StorageLocationSchema,
20371
20531
  relativePath: string(),
20372
20532
  data: _instanceof(Uint8Array)
20373
- }), _void(), { kind: "mutation" }), method(object({
20533
+ }), _void(), {
20534
+ kind: "mutation",
20535
+ auth: "admin"
20536
+ }), method(object({
20374
20537
  location: StorageLocationSchema,
20375
20538
  relativePath: string()
20376
- }), _instanceof(Uint8Array)), method(object({
20539
+ }), _instanceof(Uint8Array), { auth: "admin" }), method(object({
20377
20540
  location: StorageLocationSchema,
20378
20541
  relativePath: string()
20379
- }), boolean()), method(object({
20542
+ }), boolean(), { auth: "admin" }), method(object({
20380
20543
  location: StorageLocationSchema,
20381
20544
  prefix: string().optional()
20382
- }), array(string()).readonly()), method(object({
20545
+ }), array(string()).readonly(), { auth: "admin" }), method(object({
20383
20546
  location: StorageLocationSchema,
20384
20547
  relativePath: string()
20385
- }), _void(), { kind: "mutation" }), method(object({ location: StorageLocationSchema }), number().nullable()), method(BeginUploadInputSchema, BeginUploadResultSchema, { kind: "mutation" }), method(WriteChunkInputSchema, _void(), { kind: "mutation" }), method(FinalizeUploadInputSchema, _void(), { kind: "mutation" }), method(AbortUploadInputSchema, _void(), { kind: "mutation" }), method(BeginDownloadInputSchema, BeginDownloadResultSchema, { kind: "mutation" }), method(ReadChunkInputSchema, _instanceof(Uint8Array)), method(EndDownloadInputSchema, _void(), { kind: "mutation" });
20548
+ }), _void(), {
20549
+ kind: "mutation",
20550
+ auth: "admin"
20551
+ }), method(object({ location: StorageLocationSchema }), number().nullable(), { auth: "admin" }), method(BeginUploadInputSchema, BeginUploadResultSchema, {
20552
+ kind: "mutation",
20553
+ auth: "admin"
20554
+ }), method(WriteChunkInputSchema, _void(), {
20555
+ kind: "mutation",
20556
+ auth: "admin"
20557
+ }), method(FinalizeUploadInputSchema, _void(), {
20558
+ kind: "mutation",
20559
+ auth: "admin"
20560
+ }), method(AbortUploadInputSchema, _void(), {
20561
+ kind: "mutation",
20562
+ auth: "admin"
20563
+ }), method(BeginDownloadInputSchema, BeginDownloadResultSchema, {
20564
+ kind: "mutation",
20565
+ auth: "admin"
20566
+ }), method(ReadChunkInputSchema, _instanceof(Uint8Array), { auth: "admin" }), method(EndDownloadInputSchema, _void(), {
20567
+ kind: "mutation",
20568
+ auth: "admin"
20569
+ });
20386
20570
  /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
20387
20571
  var ProfileSettingsSchemaBridge = unknown().nullable();
20388
20572
  var ProfileSettingsBagSchema = record(string(), unknown());
@@ -20640,7 +20824,8 @@ method(object({
20640
20824
  access: "create"
20641
20825
  }), method(object({ userId: string().optional() }), object({ optionsJSON: record(string(), unknown()) }), {
20642
20826
  kind: "mutation",
20643
- access: "view"
20827
+ access: "view",
20828
+ auth: "admin"
20644
20829
  }), method(object({
20645
20830
  /** Required — the user the assertion belongs to (verified). */
20646
20831
  userId: string(),
@@ -20648,10 +20833,12 @@ method(object({
20648
20833
  response: record(string(), unknown())
20649
20834
  }), object({ verified: boolean() }), {
20650
20835
  kind: "mutation",
20651
- access: "view"
20836
+ access: "view",
20837
+ auth: "admin"
20652
20838
  }), method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
20653
20839
  kind: "mutation",
20654
- access: "view"
20840
+ access: "view",
20841
+ auth: "admin"
20655
20842
  }), method(object({
20656
20843
  /** AuthenticationResponseJSON from the browser. */
20657
20844
  response: record(string(), unknown()) }), object({
@@ -20659,7 +20846,8 @@ response: record(string(), unknown()) }), object({
20659
20846
  userId: string().nullable()
20660
20847
  }), {
20661
20848
  kind: "mutation",
20662
- access: "view"
20849
+ access: "view",
20850
+ auth: "admin"
20663
20851
  }), method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }), method(object({
20664
20852
  userId: string(),
20665
20853
  credentialId: string()
@@ -20831,7 +21019,19 @@ var VectorStatsResultSchema = object({
20831
21019
  /** False when the backend ranks approximately. */
20832
21020
  exact: boolean()
20833
21021
  });
20834
- method(VectorDeclareIndexInputSchema, _void(), { kind: "mutation" }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, { kind: "mutation" }), method(VectorQueryInputSchema, VectorQueryResultSchema), method(VectorGetInputSchema, VectorGetResultSchema), method(VectorDeleteInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorStatsInputSchema, VectorStatsResultSchema);
21022
+ method(VectorDeclareIndexInputSchema, _void(), {
21023
+ kind: "mutation",
21024
+ auth: "admin"
21025
+ }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
21026
+ kind: "mutation",
21027
+ auth: "admin"
21028
+ }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
21029
+ kind: "mutation",
21030
+ auth: "admin"
21031
+ }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
21032
+ kind: "mutation",
21033
+ auth: "admin"
21034
+ }), method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" });
20835
21035
  var ClipSchema = object({
20836
21036
  /** Opaque, provider-namespaced id. The default provider encodes the time
20837
21037
  * window so `getClipPlayback` is self-contained (no event re-query). */
@@ -22574,7 +22774,27 @@ var MediaFileLiteSchema$1 = object({
22574
22774
  sizeBytes: number(),
22575
22775
  timestamp: number()
22576
22776
  });
22577
- method(_void(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
22777
+ method(object({
22778
+ /**
22779
+ * Inline {@link IdentitySchema.coverBase64} on every row.
22780
+ *
22781
+ * Default `false`, the same inversion `listRecentFaces` and
22782
+ * `listPlates` took on 2026-08-25 (see `include-crops-default.ts` for
22783
+ * why the burden belongs on the caller that WANTS the bytes). Measured
22784
+ * on the live hub the same day: four identities cost 40,979 B with the
22785
+ * covers inline, ~10 KB of base64 per row, on a query this UI mounts
22786
+ * four times and the viewer holds at `staleTime: 30_000`.
22787
+ *
22788
+ * Nothing loses its avatar: `coverMediaKey` is already on every row and
22789
+ * the `event-media` plane serves that key `immutable` with an ETag.
22790
+ *
22791
+ * **This is an INPUT field, so it does not reach the addon until the
22792
+ * next train** — the hub router validates cap inputs against its own
22793
+ * compiled Zod and strips a key it does not know. Until then the
22794
+ * provider sees `undefined`, which resolves to `false`: the cheap shape
22795
+ * is what ships, and the opt-in becomes reachable when the train lands.
22796
+ */
22797
+ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
22578
22798
  kind: "mutation",
22579
22799
  auth: "admin"
22580
22800
  }), method(object({
@@ -24747,7 +24967,14 @@ var PlateInfoSchema = object({
24747
24967
  plateBbox: BoundingBoxSchema.optional(),
24748
24968
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
24749
24969
  keyFrameMediaKey: string().optional(),
24750
- base64: string().optional()
24970
+ base64: string().optional(),
24971
+ /**
24972
+ * Same crop as a data-plane URL, always present when the plate has a stored
24973
+ * crop. `getPlateByTrack` returns this and never inlines JPEG; `listPlates`
24974
+ * and `searchPlates` inline the crop only when their `includeCrops` input is
24975
+ * left at its `true` default.
24976
+ */
24977
+ cropUrl: string().optional()
24751
24978
  });
24752
24979
  var MediaFileLiteSchema = object({
24753
24980
  key: string(),
@@ -24765,14 +24992,34 @@ var PlateClusterSchema = object({
24765
24992
  });
24766
24993
  method(object({
24767
24994
  deviceId: number().int().optional(),
24768
- limit: number().int().positive().optional()
24995
+ limit: number().int().positive().optional(),
24996
+ /**
24997
+ * Inline the base64 crop on every row. Default `true` — the existing
24998
+ * behaviour, kept so no caller breaks.
24999
+ *
25000
+ * Set `false` once the caller renders {@link PlateInfo.cropUrl}.
25001
+ * Measured on the live hub at the 500 rows the Plates view asks for:
25002
+ * 879,403 B and 27.7 s with the crops inline, against a few KiB of
25003
+ * metadata without them — and the browser then caches the images.
25004
+ *
25005
+ * This is the plate twin of `faceGallery.listRecentFaces`'s option;
25006
+ * plates were the one gallery list left without it.
25007
+ *
25008
+ * **This is an INPUT field, so it does not reach the addon until the
25009
+ * next train.** The hub router validates cap inputs against its own
25010
+ * compiled Zod and strips a key it does not know. Until the train
25011
+ * ships, sending `false` is harmless and keeps the crops inline.
25012
+ */
25013
+ includeCrops: boolean().optional()
24769
25014
  }).optional(), array(PlateInfoSchema).readonly()), method(object({
24770
25015
  deviceId: number().int(),
24771
25016
  trackId: string()
24772
25017
  }), PlateInfoSchema.nullable()), method(object({ plateId: string() }), array(MediaFileLiteSchema).readonly()), method(object({
24773
25018
  text: string().min(1),
24774
25019
  maxDistance: number().int().min(0).optional(),
24775
- limit: number().int().positive().optional()
25020
+ limit: number().int().positive().optional(),
25021
+ /** See `listPlates.includeCrops`. Default `true`. */
25022
+ includeCrops: boolean().optional()
24776
25023
  }), array(PlateInfoSchema).readonly()), method(object({
24777
25024
  maxDistance: number().int().min(0).optional(),
24778
25025
  minClusterSize: number().int().min(2).optional(),
@@ -24786,7 +25033,13 @@ method(object({
24786
25033
  }), method(object({ plateId: string() }), _void(), {
24787
25034
  kind: "mutation",
24788
25035
  auth: "admin"
24789
- }), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
25036
+ }), method(object({
25037
+ /** Inline {@link VehicleSchema.coverBase64} on every row. Default
25038
+ * `false` — the vehicle twin of `faceGallery.listIdentities`'s
25039
+ * option; `coverMediaKey` + the `event-media` plane carry the picture.
25040
+ * INPUT field: stripped by the hub router until the train ships, which
25041
+ * resolves to `false` and is exactly the intended default. */
25042
+ includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
24790
25043
  kind: "mutation",
24791
25044
  auth: "admin"
24792
25045
  }), method(object({
@@ -28412,6 +28665,12 @@ Object.freeze({
28412
28665
  addonId: null,
28413
28666
  access: "view"
28414
28667
  },
28668
+ "deviceManager.getBindingsBatch": {
28669
+ capName: "device-manager",
28670
+ capScope: "system",
28671
+ addonId: null,
28672
+ access: "view"
28673
+ },
28415
28674
  "deviceManager.getChildren": {
28416
28675
  capName: "device-manager",
28417
28676
  capScope: "system",
@@ -28472,6 +28731,12 @@ Object.freeze({
28472
28731
  addonId: null,
28473
28732
  access: "view"
28474
28733
  },
28734
+ "deviceManager.getLinkedDevicesBatch": {
28735
+ capName: "device-manager",
28736
+ capScope: "system",
28737
+ addonId: null,
28738
+ access: "view"
28739
+ },
28475
28740
  "deviceManager.getRoleDisplayDefaults": {
28476
28741
  capName: "device-manager",
28477
28742
  capScope: "system",
@@ -30176,6 +30441,12 @@ Object.freeze({
30176
30441
  addonId: null,
30177
30442
  access: "create"
30178
30443
  },
30444
+ "pipelineAnalytics.cancelRelocateMedia": {
30445
+ capName: "pipeline-analytics",
30446
+ capScope: "device",
30447
+ addonId: null,
30448
+ access: "create"
30449
+ },
30179
30450
  "pipelineAnalytics.cancelStorageMigrationMove": {
30180
30451
  capName: "pipeline-analytics",
30181
30452
  capScope: "device",
@@ -30242,6 +30513,12 @@ Object.freeze({
30242
30513
  addonId: null,
30243
30514
  access: "view"
30244
30515
  },
30516
+ "pipelineAnalytics.getGroup": {
30517
+ capName: "pipeline-analytics",
30518
+ capScope: "device",
30519
+ addonId: null,
30520
+ access: "view"
30521
+ },
30245
30522
  "pipelineAnalytics.getKeyEvents": {
30246
30523
  capName: "pipeline-analytics",
30247
30524
  capScope: "device",
@@ -30326,6 +30603,12 @@ Object.freeze({
30326
30603
  addonId: null,
30327
30604
  access: "view"
30328
30605
  },
30606
+ "pipelineAnalytics.listGroups": {
30607
+ capName: "pipeline-analytics",
30608
+ capScope: "device",
30609
+ addonId: null,
30610
+ access: "view"
30611
+ },
30329
30612
  "pipelineAnalytics.listOpsLog": {
30330
30613
  capName: "pipeline-analytics",
30331
30614
  capScope: "device",
@@ -30338,6 +30621,12 @@ Object.freeze({
30338
30621
  addonId: null,
30339
30622
  access: "view"
30340
30623
  },
30624
+ "pipelineAnalytics.listRelocateMediaJobs": {
30625
+ capName: "pipeline-analytics",
30626
+ capScope: "device",
30627
+ addonId: null,
30628
+ access: "view"
30629
+ },
30341
30630
  "pipelineAnalytics.listRetrainAnnotations": {
30342
30631
  capName: "pipeline-analytics",
30343
30632
  capScope: "device",
@@ -30416,6 +30705,12 @@ Object.freeze({
30416
30705
  addonId: null,
30417
30706
  access: "create"
30418
30707
  },
30708
+ "pipelineAnalytics.relocateMedia": {
30709
+ capName: "pipeline-analytics",
30710
+ capScope: "device",
30711
+ addonId: null,
30712
+ access: "create"
30713
+ },
30419
30714
  "pipelineAnalytics.restageRetrainTrack": {
30420
30715
  capName: "pipeline-analytics",
30421
30716
  capScope: "device",
@@ -33107,6 +33402,11 @@ Object.freeze({
33107
33402
  form: "single",
33108
33403
  optional: false
33109
33404
  }],
33405
+ "deviceManager.getBindingsBatch": [{
33406
+ name: "deviceIds",
33407
+ form: "array",
33408
+ optional: false
33409
+ }],
33110
33410
  "deviceManager.getChildren": [{
33111
33411
  name: "parentDeviceId",
33112
33412
  form: "single",
@@ -33152,6 +33452,11 @@ Object.freeze({
33152
33452
  form: "single",
33153
33453
  optional: false
33154
33454
  }],
33455
+ "deviceManager.getLinkedDevicesBatch": [{
33456
+ name: "deviceIds",
33457
+ form: "array",
33458
+ optional: false
33459
+ }],
33155
33460
  "deviceManager.getSettingsSchema": [{
33156
33461
  name: "deviceId",
33157
33462
  form: "single",
@@ -33172,6 +33477,11 @@ Object.freeze({
33172
33477
  form: "single",
33173
33478
  optional: false
33174
33479
  }],
33480
+ "deviceManager.listAll": [{
33481
+ name: "deviceIds",
33482
+ form: "array",
33483
+ optional: true
33484
+ }],
33175
33485
  "deviceManager.loadConfig": [{
33176
33486
  name: "deviceId",
33177
33487
  form: "single",
@@ -33745,6 +34055,11 @@ Object.freeze({
33745
34055
  form: "single",
33746
34056
  optional: false
33747
34057
  }],
34058
+ "pipelineAnalytics.getGroup": [{
34059
+ name: "deviceId",
34060
+ form: "single",
34061
+ optional: false
34062
+ }],
33748
34063
  "pipelineAnalytics.getKeyEvents": [{
33749
34064
  name: "deviceId",
33750
34065
  form: "single",
@@ -33800,6 +34115,11 @@ Object.freeze({
33800
34115
  form: "array",
33801
34116
  optional: false
33802
34117
  }],
34118
+ "pipelineAnalytics.listGroups": [{
34119
+ name: "deviceIds",
34120
+ form: "array",
34121
+ optional: false
34122
+ }],
33803
34123
  "pipelineAnalytics.listOpsLog": [{
33804
34124
  name: "deviceId",
33805
34125
  form: "single",