@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.
@@ -5886,6 +5886,13 @@ var BaseAddon = class {
5886
5886
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5887
5887
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5888
5888
  _registeredCapNames = [];
5889
+ /**
5890
+ * True only after `readAddonStore` actually answered. Constructor
5891
+ * defaults look like stored config when the store is down — a forked
5892
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5893
+ * mode, 2026-08-25) is not "the operator chose this".
5894
+ */
5895
+ settingsStoreReady = false;
5889
5896
  /** Default config values. Provided via constructor. */
5890
5897
  defaults;
5891
5898
  constructor(defaults) {
@@ -6286,7 +6293,9 @@ var BaseAddon = class {
6286
6293
  ];
6287
6294
  let lastErr;
6288
6295
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6289
- return await settings.readAddonStore() ?? {};
6296
+ const stored = await settings.readAddonStore() ?? {};
6297
+ this.settingsStoreReady = true;
6298
+ return stored;
6290
6299
  } catch (err) {
6291
6300
  lastErr = err;
6292
6301
  const msg = err instanceof Error ? err.message : String(err);
@@ -6294,6 +6303,7 @@ var BaseAddon = class {
6294
6303
  if (attempt === delaysMs.length) break;
6295
6304
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6296
6305
  }
6306
+ this.settingsStoreReady = false;
6297
6307
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6298
6308
  return {};
6299
6309
  }
@@ -6764,7 +6774,7 @@ function method(input, output, options) {
6764
6774
  input,
6765
6775
  output,
6766
6776
  kind: options?.kind ?? "query",
6767
- auth: options?.auth ?? "protected",
6777
+ ...options?.auth !== void 0 ? { auth: options.auth } : {},
6768
6778
  ...options?.access !== void 0 ? { access: options.access } : {},
6769
6779
  ...options?.caller !== void 0 ? { caller: options.caller } : {},
6770
6780
  timeoutMs: options?.timeoutMs
@@ -6784,7 +6794,7 @@ function systemMethod(input, output, options) {
6784
6794
  }
6785
6795
  var StaticDirOutputSchema$1 = object({ staticDir: string() });
6786
6796
  var VersionOutputSchema$1 = object({ version: string() });
6787
- method(_void(), StaticDirOutputSchema$1), method(_void(), VersionOutputSchema$1);
6797
+ method(_void(), StaticDirOutputSchema$1, { auth: "admin" }), method(_void(), VersionOutputSchema$1, { auth: "admin" });
6788
6798
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
6789
6799
  DeviceType["Camera"] = "camera";
6790
6800
  DeviceType["Hub"] = "hub";
@@ -7105,7 +7115,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
7105
7115
  }({});
7106
7116
  var StaticDirOutputSchema = object({ staticDir: string() });
7107
7117
  var VersionOutputSchema = object({ version: string() });
7108
- method(_void(), StaticDirOutputSchema), method(_void(), VersionOutputSchema);
7118
+ method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
7109
7119
  /**
7110
7120
  * device-ops — device-scoped cap that unifies the per-IDevice operations
7111
7121
  * previously routed through the `.device-ops` Moleculer bridge service.
@@ -8257,24 +8267,6 @@ var RecordingRetentionSchema = object({
8257
8267
  maxSizeGb: number().min(0).optional()
8258
8268
  });
8259
8269
  /**
8260
- * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
8261
- * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
8262
- * previews at. Five graduated steps; absent on a config = `standard` (the
8263
- * shipped default, matching `sheet-geometry`/`sheet-composer`).
8264
- *
8265
- * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
8266
- * Each window's index sidecar carries its own tile dims, so a camera whose
8267
- * preset changed over time renders every historical window at the dims it was
8268
- * written with.
8269
- */
8270
- var ScrubThumbnailPresetSchema = _enum([
8271
- "minimal",
8272
- "low",
8273
- "standard",
8274
- "high",
8275
- "max"
8276
- ]);
8277
- /**
8278
8270
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
8279
8271
  *
8280
8272
  * `bands` is the ONLY authored recording intent: what to record, when, and on
@@ -8282,7 +8274,11 @@ var ScrubThumbnailPresetSchema = _enum([
8282
8274
  * other field is a storage knob (profiles, segment length, retention, scrub).
8283
8275
  *
8284
8276
  * STRICT on purpose: the legacy authoring surface (`schedule`/`schedules`/
8285
- * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30.
8277
+ * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30,
8278
+ * and `scrubThumbnails` — a five-step fidelity knob for a sprite tier that was
8279
+ * deleted on 2026-07-24 and had ZERO consumers in the recorder — on 2026-08-25
8280
+ * (D62: a switch that writes a store nobody reads is worse than no switch).
8281
+ * Stored rows keep loading: the READ schema is `.strip()` (config-store.ts).
8286
8282
  * A stale caller must fail loudly — silently stripping its legacy intent would
8287
8283
  * persist a band-less config, i.e. silently stop recording the camera.
8288
8284
  */
@@ -8305,14 +8301,7 @@ var RecordingConfigSchema = object({
8305
8301
  * "off" is the absence of a covering band, never a band value.
8306
8302
  */
8307
8303
  bands: array(RecordingBandSchema).default([]),
8308
- retention: RecordingRetentionSchema.optional(),
8309
- /**
8310
- * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
8311
- * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
8312
- * windows only — existing sheets are immutable, and each window's index
8313
- * carries its own tile dims so mixed-preset history renders correctly.
8314
- */
8315
- scrubThumbnails: ScrubThumbnailPresetSchema.optional()
8304
+ retention: RecordingRetentionSchema.optional()
8316
8305
  }).strict();
8317
8306
  /**
8318
8307
  * Entity-relocation job state (storage entity-routing spec, Phase 4).
@@ -8388,10 +8377,11 @@ var RelocateFootageInputSchema = object({
8388
8377
  * `RecordingConfig.enabled` or camera wrapper bindings. */
8389
8378
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
8390
8379
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
8391
- var StorageMigrationMediaMoveInputSchema = object({
8380
+ var RelocateMediaInputSchema = object({
8392
8381
  toLocationId: string(),
8393
8382
  throttleMbps: number().min(1).max(1e3).optional()
8394
- }).extend({ leaseId: string().min(1) });
8383
+ });
8384
+ var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
8395
8385
  /** The independently selectable logical storage classes. `recordings`
8396
8386
  * encompasses the high and mid segment profiles; `recordingsLow` is low
8397
8387
  * segments; `eventMedia` is post-analysis blobs. */
@@ -8740,7 +8730,26 @@ var LabelDefinitionSchema = object({
8740
8730
  description: string().optional(),
8741
8731
  icon: string().optional()
8742
8732
  });
8743
- var ClassMapDefinitionSchema = object({
8733
+ /**
8734
+ * Wire schema for a per-model CATALOG classMap override
8735
+ * (`ModelCatalogEntry.classMap` / `ModelConvertMetadata.classMap`) —
8736
+ * restricted to {@link CLASS_MAP_MACRO_TARGETS}, the only macros the
8737
+ * detection pipeline executor actually routes.
8738
+ *
8739
+ * This is deliberately a DIFFERENT, narrower shape than the general-purpose
8740
+ * `ClassMapDefinition` interface above (e.g. `IDetectionAddon.getClassMap()`
8741
+ * and the audio `YAMNET_TO_MACRO` catalog both use macro targets outside this
8742
+ * enum) — the two used to share the name `ClassMapDefinition`/
8743
+ * `ClassMapDefinitionSchema`, which made the schema-type-twin guard
8744
+ * (`scripts/check-schema-type-twins.ts`) flag them as a duplicated shape. They
8745
+ * are not: it is two different concepts colliding on a name. Keep this type
8746
+ * under its own name rather than reusing `ClassMapDefinition` — reusing it
8747
+ * would either narrow every `ClassMapDefinition` consumer to the four
8748
+ * detection macros (breaking `YAMNET_TO_MACRO`) or drop the validation this
8749
+ * schema exists for (see the "rejects a classMap whose target is not a
8750
+ * detection macro" test in `model-catalog-schema.test.ts`).
8751
+ */
8752
+ var DetectionCatalogClassMapSchema = object({
8744
8753
  mapping: record(string(), _enum([
8745
8754
  "person",
8746
8755
  "vehicle",
@@ -8832,6 +8841,12 @@ var ModelVariantGroupSchema = object({
8832
8841
  */
8833
8842
  resolution: number().int().positive().optional()
8834
8843
  });
8844
+ var ModelProviderIdSchema = _enum([
8845
+ "camstack",
8846
+ "frigate",
8847
+ "scrypted",
8848
+ "custom"
8849
+ ]);
8835
8850
  var ModelCatalogEntrySchema = object({
8836
8851
  id: string(),
8837
8852
  name: string(),
@@ -8929,11 +8944,17 @@ var ModelCatalogEntrySchema = object({
8929
8944
  */
8930
8945
  group: ModelVariantGroupSchema.optional(),
8931
8946
  /**
8947
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8948
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8949
+ * persisted before this field existed (`inferModelProvider` fills those).
8950
+ */
8951
+ provider: ModelProviderIdSchema.optional(),
8952
+ /**
8932
8953
  * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8933
8954
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8934
8955
  * labels already ARE the CamStack macros (Scrypted identity map).
8935
8956
  */
8936
- classMap: ClassMapDefinitionSchema.optional()
8957
+ classMap: DetectionCatalogClassMapSchema.optional()
8937
8958
  });
8938
8959
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8939
8960
  format: literal("openvino"),
@@ -8963,7 +8984,7 @@ var ModelConvertMetadataSchema = object({
8963
8984
  "segmentation"
8964
8985
  ]),
8965
8986
  faceAlignment: boolean().optional(),
8966
- classMap: ClassMapDefinitionSchema.optional()
8987
+ classMap: DetectionCatalogClassMapSchema.optional()
8967
8988
  });
8968
8989
  var ConvertResultSchema = object({
8969
8990
  entry: ModelCatalogEntrySchema,
@@ -9826,7 +9847,7 @@ var AddonPageDeclarationSchema = object({
9826
9847
  /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
9827
9848
  sectionLabel: string().optional()
9828
9849
  });
9829
- method(_void(), array(AddonPageDeclarationSchema).readonly());
9850
+ method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
9830
9851
  var AddonHttpRouteSchema = object({
9831
9852
  method: _enum([
9832
9853
  "GET",
@@ -10061,7 +10082,7 @@ var WidgetMetadataSchema = object({
10061
10082
  defaultColumns: number().int().min(1).max(12).default(6),
10062
10083
  defaultRows: number().int().min(1).max(12).default(1)
10063
10084
  });
10064
- method(_void(), array(WidgetMetadataSchema).readonly());
10085
+ method(_void(), array(WidgetMetadataSchema).readonly(), { auth: "admin" });
10065
10086
  /**
10066
10087
  * `addon-widgets` — system-scoped singleton aggregator cap. Public-facing
10067
10088
  * surface that admin-ui consumes through `useAddonWidgetsListWidgets()`.
@@ -11583,7 +11604,7 @@ var CustomModelDescriptorSchema = object({
11583
11604
  stepId: string(),
11584
11605
  entry: ModelCatalogEntrySchema
11585
11606
  });
11586
- method(_void(), array(CustomModelDescriptorSchema).readonly());
11607
+ method(_void(), array(CustomModelDescriptorSchema).readonly(), { auth: "admin" });
11587
11608
  /**
11588
11609
  * Query filter for settings-store collections.
11589
11610
  */
@@ -11670,7 +11691,8 @@ method(object({
11670
11691
  }), _void(), { kind: "mutation" }), method(object({
11671
11692
  namespace: string().optional(),
11672
11693
  collection: string(),
11673
- filter: QueryFilterSchema.optional()
11694
+ filter: QueryFilterSchema.optional(),
11695
+ columns: array(string()).readonly().optional()
11674
11696
  }), array(SettingsRecordSchema).readonly()), method(object({
11675
11697
  namespace: string().optional(),
11676
11698
  collection: string(),
@@ -11733,46 +11755,87 @@ var EngineInfoSchema = object({
11733
11755
  kind: _enum(["relational", "vector"]),
11734
11756
  displayName: string()
11735
11757
  });
11736
- method(_void(), EngineInfoSchema), method(object({
11758
+ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
11737
11759
  namespace: string().optional(),
11738
11760
  collection: string(),
11739
11761
  key: string()
11740
- }), unknown()), method(object({
11762
+ }), unknown(), { auth: "admin" }), method(object({
11741
11763
  namespace: string().optional(),
11742
11764
  collection: string(),
11743
11765
  key: string(),
11744
11766
  value: unknown()
11745
- }), _void(), { kind: "mutation" }), method(object({
11767
+ }), _void(), {
11768
+ kind: "mutation",
11769
+ auth: "admin"
11770
+ }), method(object({
11746
11771
  namespace: string().optional(),
11747
11772
  collection: string(),
11748
- filter: QueryFilterSchema.optional()
11749
- }), array(SettingsRecordSchema).readonly()), method(object({
11773
+ filter: QueryFilterSchema.optional(),
11774
+ /**
11775
+ * SQL-level column projection — MUST mirror `settings-store.query`.
11776
+ *
11777
+ * ⚠ An earlier version of this comment blamed Zod stripping, and that
11778
+ * was wrong — corrected 2026-08-26 after the hop map
11779
+ * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
11780
+ * There is **no Zod parse at all** between the door and the engine: the
11781
+ * dispatcher forwards the payload verbatim and UDS carries it whole. A
11782
+ * field declared here reaches `SqliteSettingsBackend` either way.
11783
+ *
11784
+ * What actually lost `columns` was the THIRD declaration of this shape:
11785
+ * `SettingsQueryInput` in `interfaces/storage.ts`, a hand-written TS
11786
+ * interface the engine destructures from. The field existed on both
11787
+ * schemas and the engine still never read it, because nothing checks a
11788
+ * registered provider against `InferProvider<cap>` —
11789
+ * `ProviderRegistration.provider` is typed `object`.
11790
+ *
11791
+ * It is declared here anyway, and must stay in step with
11792
+ * `settings-store.query`: a caller reading only the cap definitions has
11793
+ * to be able to see that this call carries a projection.
11794
+ * `data-door-schema-parity.spec.ts` keeps the two aligned.
11795
+ */
11796
+ columns: array(string()).readonly().optional()
11797
+ }), array(SettingsRecordSchema).readonly(), { auth: "admin" }), method(object({
11750
11798
  namespace: string().optional(),
11751
11799
  collection: string(),
11752
11800
  record: SettingsRecordSchema
11753
- }), _void(), { kind: "mutation" }), method(object({
11801
+ }), _void(), {
11802
+ kind: "mutation",
11803
+ auth: "admin"
11804
+ }), method(object({
11754
11805
  namespace: string().optional(),
11755
11806
  collection: string(),
11756
11807
  id: string(),
11757
11808
  data: record(string(), unknown())
11758
- }), _void(), { kind: "mutation" }), method(object({
11809
+ }), _void(), {
11810
+ kind: "mutation",
11811
+ auth: "admin"
11812
+ }), method(object({
11759
11813
  namespace: string().optional(),
11760
11814
  collection: string(),
11761
11815
  key: string()
11762
- }), _void(), { kind: "mutation" }), method(object({
11816
+ }), _void(), {
11817
+ kind: "mutation",
11818
+ auth: "admin"
11819
+ }), method(object({
11763
11820
  namespace: string().optional(),
11764
11821
  collection: string(),
11765
11822
  filter: MutationFilterSchema
11766
- }), object({ deleted: number().int() }), { kind: "mutation" }), method(object({
11823
+ }), object({ deleted: number().int() }), {
11824
+ kind: "mutation",
11825
+ auth: "admin"
11826
+ }), method(object({
11767
11827
  namespace: string().optional(),
11768
11828
  collection: string(),
11769
11829
  filter: MutationFilterSchema,
11770
11830
  data: record(string(), unknown())
11771
- }), object({ updated: number().int() }), { kind: "mutation" }), method(object({
11831
+ }), object({ updated: number().int() }), {
11832
+ kind: "mutation",
11833
+ auth: "admin"
11834
+ }), method(object({
11772
11835
  namespace: string().optional(),
11773
11836
  collection: string(),
11774
11837
  filter: QueryFilterSchema.optional()
11775
- }), number()), method(object({
11838
+ }), number(), { auth: "admin" }), method(object({
11776
11839
  namespace: string().optional(),
11777
11840
  collection: string(),
11778
11841
  field: string(),
@@ -11782,15 +11845,18 @@ method(_void(), EngineInfoSchema), method(object({
11782
11845
  }), array(object({
11783
11846
  bucket: number().int(),
11784
11847
  count: number().int()
11785
- })).readonly()), method(object({
11848
+ })).readonly(), { auth: "admin" }), method(object({
11786
11849
  namespace: string().optional(),
11787
11850
  collection: string()
11788
- }), boolean()), method(object({
11851
+ }), boolean(), { auth: "admin" }), method(object({
11789
11852
  namespace: string().optional(),
11790
11853
  collection: string(),
11791
11854
  columns: array(CollectionColumnSchema).readonly(),
11792
11855
  indexes: array(CollectionIndexSchema).readonly().optional()
11793
- }), _void(), { kind: "mutation" });
11856
+ }), _void(), {
11857
+ kind: "mutation",
11858
+ auth: "admin"
11859
+ });
11794
11860
  /**
11795
11861
  * Stable UI option list for the `hwaccel` setting. Decoder addons
11796
11862
  * reuse this for `globalSettingsSchema()` so the dropdown is
@@ -12537,6 +12603,27 @@ var LinkedDeviceSchema = object({
12537
12603
  features: array(string()),
12538
12604
  producesTrackedEvents: boolean().optional()
12539
12605
  });
12606
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
12607
+ * The batch answer needs the tag; the single-device answer already has it
12608
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
12609
+ var LinkedDevicesForDeviceSchema = object({
12610
+ deviceId: number(),
12611
+ mode: LinkedDevicesModeSchema,
12612
+ devices: array(LinkedDeviceSchema)
12613
+ });
12614
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
12615
+ * `getAllBindings` all answer in. Declared once: three copies of the same
12616
+ * object literal is exactly how the three drift apart. */
12617
+ var DeviceBindingsForDeviceSchema = object({
12618
+ deviceId: number(),
12619
+ entries: array(object({
12620
+ capName: string(),
12621
+ kind: _enum(["native", "wrapped"]),
12622
+ providerAddonId: string(),
12623
+ providerNodeId: string(),
12624
+ nativeAddonId: string()
12625
+ }))
12626
+ });
12540
12627
  var SavedDeviceRowSchema = object({
12541
12628
  /** Numeric id reserved at allocateDeviceId time. */
12542
12629
  id: number(),
@@ -12762,11 +12849,25 @@ method(object({
12762
12849
  projection: _enum(["full", "slim"]).optional(),
12763
12850
  /** Return only camera devices. Filtering server-side instead of
12764
12851
  * shipping 293 rows to find 12. */
12765
- isCamera: boolean().optional()
12852
+ isCamera: boolean().optional(),
12853
+ /**
12854
+ * Return only these device ids. For the caller that already KNOWS the
12855
+ * handful it wants and needs a field the id-bearing answer does not
12856
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12857
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12858
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12859
+ * refetches on the reconcile interval, on a phone.
12860
+ *
12861
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12862
+ * keys rather than rejecting them (verified against the live hub
12863
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12864
+ * it answers today and the caller filters as it already does.
12865
+ */
12866
+ deviceIds: array(number()).optional()
12766
12867
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12767
12868
  mode: LinkedDevicesModeSchema,
12768
12869
  devices: array(LinkedDeviceSchema)
12769
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12870
+ })), 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({
12770
12871
  deviceId: number(),
12771
12872
  values: record(string(), unknown())
12772
12873
  }), object({ success: literal(true) }), {
@@ -12793,25 +12894,7 @@ method(object({
12793
12894
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
12794
12895
  kind: "mutation",
12795
12896
  auth: "admin"
12796
- }), method(object({ deviceId: number() }), object({
12797
- deviceId: number(),
12798
- entries: array(object({
12799
- capName: string(),
12800
- kind: _enum(["native", "wrapped"]),
12801
- providerAddonId: string(),
12802
- providerNodeId: string(),
12803
- nativeAddonId: string()
12804
- }))
12805
- })), method(object({}), array(object({
12806
- deviceId: number(),
12807
- entries: array(object({
12808
- capName: string(),
12809
- kind: _enum(["native", "wrapped"]),
12810
- providerAddonId: string(),
12811
- providerNodeId: string(),
12812
- nativeAddonId: string()
12813
- }))
12814
- }))), method(object({
12897
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12815
12898
  deviceId: number(),
12816
12899
  capName: string(),
12817
12900
  wrapperAddonId: string(),
@@ -12982,7 +13065,7 @@ method(object({
12982
13065
  crop: _instanceof(Uint8Array),
12983
13066
  width: number(),
12984
13067
  height: number()
12985
- }), EmbeddingResultSchema), method(object({ text: string() }), EmbeddingResultSchema), method(_void(), EmbeddingInfoSchema);
13068
+ }), EmbeddingResultSchema, { auth: "admin" }), method(object({ text: string() }), EmbeddingResultSchema, { auth: "admin" }), method(_void(), EmbeddingInfoSchema, { auth: "admin" });
12986
13069
  /**
12987
13070
  * filesystem-browse — per-node capability for browsing the node's local
12988
13071
  * filesystem. Reads are unconfined (whole filesystem, from `/` down); WRITES
@@ -13275,19 +13358,22 @@ method(LlmGenerateBaseInputSchema.extend({
13275
13358
  runtime: ManagedRuntimeConfigSchema,
13276
13359
  /** The managed profile's timeout, threaded by the hub provider. */
13277
13360
  timeoutMs: number().int().positive().optional()
13278
- }), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
13361
+ }), LlmGenerateResultSchema, {
13362
+ kind: "mutation",
13363
+ auth: "admin"
13364
+ }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
13279
13365
  kind: "mutation",
13280
13366
  auth: "admin"
13281
13367
  }), method(object({}), _void(), {
13282
13368
  kind: "mutation",
13283
13369
  auth: "admin"
13284
- }), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
13370
+ }), method(object({}), LlmRuntimeStatusSchema, { auth: "admin" }), method(object({ model: ManagedModelRefSchema }), _void(), {
13285
13371
  kind: "mutation",
13286
13372
  auth: "admin"
13287
13373
  }), method(object({ file: string() }), _void(), {
13288
13374
  kind: "mutation",
13289
13375
  auth: "admin"
13290
- }), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
13376
+ }), method(object({}), array(LlmNodeModelSchema), { auth: "admin" }), method(object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" });
13291
13377
  /**
13292
13378
  * `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
13293
13379
  * methods concat-fan across providers; single-row methods route to ONE
@@ -15183,12 +15269,15 @@ var NcOccupancyConditionSchema = object({
15183
15269
  * there is no second switch that can disagree with the first and every rule
15184
15270
  * authored before the decision migrates for free (`audioModeOf`):
15185
15271
  *
15186
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
15187
- * classifier labels with one of them. No window, no percentage:
15188
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
15189
- * `throttle` cooldown is the only brake. The per-label confidence floor is
15190
- * the analyzer's (`classificationMinScore`, per device) a label only
15191
- * reaches this condition if the classifier was already confident enough.
15272
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
15273
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
15274
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
15275
+ * frames is the wrong question for a classifier that labels 1–3 frames
15276
+ * per episode. The count window is the brake that drops a single-frame
15277
+ * false positive; the rule's own `throttle` cooldown is the other. The
15278
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
15279
+ * per device) — a label only reaches this condition if the classifier was
15280
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
15192
15281
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
15193
15282
  * the condition: at least `hitPercent`% of the samples over
15194
15283
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -15215,14 +15304,22 @@ var NcOccupancyConditionSchema = object({
15215
15304
  * an operator who typed `dog` mean the same thing.
15216
15305
  */
15217
15306
  var NcAudioConditionSchema = object({
15218
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
15307
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
15219
15308
  labels: array(string().min(1)).min(1).optional(),
15220
15309
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
15221
15310
  dbThreshold: number().min(-96).max(0).optional(),
15222
15311
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
15223
15312
  hitPercent: number().int().min(1).max(100).default(60),
15224
15313
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
15225
- samplingSeconds: number().int().min(1).max(300).default(10)
15314
+ samplingSeconds: number().int().min(1).max(300).default(10),
15315
+ /**
15316
+ * LABEL MODE: how many labelled frames must land inside
15317
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
15318
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
15319
+ */
15320
+ confirmHits: number().int().min(1).max(20).optional(),
15321
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
15322
+ confirmWindowSec: number().int().min(1).max(60).optional()
15226
15323
  });
15227
15324
  /**
15228
15325
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -16749,7 +16846,7 @@ var OauthIntegrationDescriptorSchema = object({
16749
16846
  */
16750
16847
  refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
16751
16848
  });
16752
- method(_void(), OauthIntegrationDescriptorSchema);
16849
+ method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
16753
16850
  /**
16754
16851
  * pipeline-analytics — device-scoped wrapper cap. Refines raw
16755
16852
  * per-frame detections emitted by the pipeline runner into tracked
@@ -17596,6 +17693,46 @@ var RecentTracksPageSchema = object({
17596
17693
  /** Cursor for the next page, or null when this page is the last. */
17597
17694
  nextCursor: string().nullable()
17598
17695
  });
17696
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
17697
+ var LIST_GROUPS_MAX_LIMIT = 100;
17698
+ var AnalyticsGroupRecordSchema = object({
17699
+ id: string(),
17700
+ deviceId: number().int(),
17701
+ openedAt: number().int(),
17702
+ closedAt: number().int(),
17703
+ timestamp: number().int(),
17704
+ memberCount: number().int(),
17705
+ memberTrackIds: array(string()).readonly(),
17706
+ className: string(),
17707
+ classes: array(string()).readonly(),
17708
+ /** Relative event-media path, or null when the group has no picture yet. */
17709
+ mediaUrl: string().nullable(),
17710
+ singleton: boolean()
17711
+ });
17712
+ var AnalyticsGroupMemberSchema = object({
17713
+ trackId: string(),
17714
+ deviceId: number().int(),
17715
+ className: string(),
17716
+ firstSeen: number().int(),
17717
+ lastSeen: number().int(),
17718
+ mediaUrl: string().nullable()
17719
+ });
17720
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
17721
+ var ListGroupsQueryInput = object({
17722
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
17723
+ deviceIds: array(number()),
17724
+ /** Window lower bound on `closedAt` (inclusive). */
17725
+ since: number().optional(),
17726
+ /** Window upper bound on `openedAt` (inclusive). */
17727
+ until: number().optional(),
17728
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
17729
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
17730
+ cursor: string().optional()
17731
+ });
17732
+ var ListGroupsPageSchema = object({
17733
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
17734
+ nextCursor: string().nullable()
17735
+ });
17599
17736
  var KeyEventQueryInput = object({
17600
17737
  deviceId: number(),
17601
17738
  /** Window lower bound (track firstSeen ≥ since). */
@@ -17671,7 +17808,9 @@ var TrackCascadeCountsSchema = object({
17671
17808
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
17672
17809
  plates: number().int(),
17673
17810
  /** Per-track CLIP search vectors removed (best-effort). */
17674
- embeddings: number().int()
17811
+ embeddings: number().int(),
17812
+ /** Group membership + group rows removed with their last member (best-effort). */
17813
+ groups: number().int()
17675
17814
  });
17676
17815
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
17677
17816
  var DiskReconcileCountsSchema = object({
@@ -17817,7 +17956,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17817
17956
  * stationary registry). Default false: the timeline lists passages,
17818
17957
  * not parking records (operator decision, 2026-08-15). */
17819
17958
  includeStationary: boolean().optional()
17820
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17959
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17960
+ deviceId: number(),
17961
+ groupId: string().min(1)
17962
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17821
17963
  kind: "mutation",
17822
17964
  auth: "admin"
17823
17965
  }), 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({
@@ -17899,6 +18041,15 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17899
18041
  }), method(object({ jobId: string() }), RelocateJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
17900
18042
  kind: "mutation",
17901
18043
  auth: "admin"
18044
+ }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
18045
+ kind: "mutation",
18046
+ auth: "admin"
18047
+ }), method(object({}), array(RelocateJobSchema).readonly(), {
18048
+ kind: "query",
18049
+ auth: "admin"
18050
+ }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
18051
+ kind: "mutation",
18052
+ auth: "admin"
17902
18053
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
17903
18054
  kind: "query",
17904
18055
  auth: "admin"
@@ -18053,6 +18204,11 @@ object({
18053
18204
  "jpeg"
18054
18205
  ])
18055
18206
  });
18207
+ /**
18208
+ * Process-local frame identity. It is serializable so it can ride an in-process
18209
+ * capability call, but `registryId` deliberately prevents resolution in any
18210
+ * other process or execution group.
18211
+ */
18056
18212
  var FrameRefSchema = object({
18057
18213
  registryId: string().min(1),
18058
18214
  id: string().min(1),
@@ -18127,7 +18283,8 @@ var PipelineModelOptionSchema = object({
18127
18283
  sizeMB: number()
18128
18284
  })),
18129
18285
  group: ModelVariantGroupSchema.optional(),
18130
- legacy: boolean().optional()
18286
+ legacy: boolean().optional(),
18287
+ provider: ModelProviderIdSchema.optional()
18131
18288
  });
18132
18289
  var ConfigFieldBridge = custom();
18133
18290
  var PipelineAddonSchemaSchema = object({
@@ -20217,7 +20374,7 @@ method(object({
20217
20374
  * linking rather than produce an eternal token.
20218
20375
  */
20219
20376
  ttlSec: union([number().int().positive(), literal("never")]).optional()
20220
- }), object({ token: string() })), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable());
20377
+ }), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
20221
20378
  var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
20222
20379
  providerId: string().min(1),
20223
20380
  displayName: string().min(1),
@@ -20312,10 +20469,13 @@ var EvictResultSchema = object({
20312
20469
  /** True when the provider has nothing left it is willing to drop on this location. */
20313
20470
  exhausted: boolean()
20314
20471
  });
20315
- method(object({ locationId: string() }), EvictableUsageSchema), method(object({
20472
+ method(object({ locationId: string() }), EvictableUsageSchema, { auth: "admin" }), method(object({
20316
20473
  locationId: string(),
20317
20474
  targetBytes: number().int().positive()
20318
- }), EvictResultSchema, { kind: "mutation" });
20475
+ }), EvictResultSchema, {
20476
+ kind: "mutation",
20477
+ auth: "admin"
20478
+ });
20319
20479
  method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin" }), method(StorageMigrationInputSchema, object({ jobId: string() }), {
20320
20480
  kind: "mutation",
20321
20481
  auth: "admin"
@@ -20375,26 +20535,50 @@ var ReadChunkInputSchema = object({
20375
20535
  length: number()
20376
20536
  });
20377
20537
  var EndDownloadInputSchema = object({ downloadId: string() });
20378
- method(_void(), ProviderInfoSchema), method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }), method(object({
20538
+ method(_void(), ProviderInfoSchema, { auth: "admin" }), method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }), method(object({
20379
20539
  location: StorageLocationSchema,
20380
20540
  relativePath: string()
20381
- }), string()), method(object({
20541
+ }), string(), { auth: "admin" }), method(object({
20382
20542
  location: StorageLocationSchema,
20383
20543
  relativePath: string(),
20384
20544
  data: _instanceof(Uint8Array)
20385
- }), _void(), { kind: "mutation" }), method(object({
20545
+ }), _void(), {
20546
+ kind: "mutation",
20547
+ auth: "admin"
20548
+ }), method(object({
20386
20549
  location: StorageLocationSchema,
20387
20550
  relativePath: string()
20388
- }), _instanceof(Uint8Array)), method(object({
20551
+ }), _instanceof(Uint8Array), { auth: "admin" }), method(object({
20389
20552
  location: StorageLocationSchema,
20390
20553
  relativePath: string()
20391
- }), boolean()), method(object({
20554
+ }), boolean(), { auth: "admin" }), method(object({
20392
20555
  location: StorageLocationSchema,
20393
20556
  prefix: string().optional()
20394
- }), array(string()).readonly()), method(object({
20557
+ }), array(string()).readonly(), { auth: "admin" }), method(object({
20395
20558
  location: StorageLocationSchema,
20396
20559
  relativePath: string()
20397
- }), _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" });
20560
+ }), _void(), {
20561
+ kind: "mutation",
20562
+ auth: "admin"
20563
+ }), method(object({ location: StorageLocationSchema }), number().nullable(), { auth: "admin" }), method(BeginUploadInputSchema, BeginUploadResultSchema, {
20564
+ kind: "mutation",
20565
+ auth: "admin"
20566
+ }), method(WriteChunkInputSchema, _void(), {
20567
+ kind: "mutation",
20568
+ auth: "admin"
20569
+ }), method(FinalizeUploadInputSchema, _void(), {
20570
+ kind: "mutation",
20571
+ auth: "admin"
20572
+ }), method(AbortUploadInputSchema, _void(), {
20573
+ kind: "mutation",
20574
+ auth: "admin"
20575
+ }), method(BeginDownloadInputSchema, BeginDownloadResultSchema, {
20576
+ kind: "mutation",
20577
+ auth: "admin"
20578
+ }), method(ReadChunkInputSchema, _instanceof(Uint8Array), { auth: "admin" }), method(EndDownloadInputSchema, _void(), {
20579
+ kind: "mutation",
20580
+ auth: "admin"
20581
+ });
20398
20582
  /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
20399
20583
  var ProfileSettingsSchemaBridge = unknown().nullable();
20400
20584
  var ProfileSettingsBagSchema = record(string(), unknown());
@@ -20652,7 +20836,8 @@ method(object({
20652
20836
  access: "create"
20653
20837
  }), method(object({ userId: string().optional() }), object({ optionsJSON: record(string(), unknown()) }), {
20654
20838
  kind: "mutation",
20655
- access: "view"
20839
+ access: "view",
20840
+ auth: "admin"
20656
20841
  }), method(object({
20657
20842
  /** Required — the user the assertion belongs to (verified). */
20658
20843
  userId: string(),
@@ -20660,10 +20845,12 @@ method(object({
20660
20845
  response: record(string(), unknown())
20661
20846
  }), object({ verified: boolean() }), {
20662
20847
  kind: "mutation",
20663
- access: "view"
20848
+ access: "view",
20849
+ auth: "admin"
20664
20850
  }), method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
20665
20851
  kind: "mutation",
20666
- access: "view"
20852
+ access: "view",
20853
+ auth: "admin"
20667
20854
  }), method(object({
20668
20855
  /** AuthenticationResponseJSON from the browser. */
20669
20856
  response: record(string(), unknown()) }), object({
@@ -20671,7 +20858,8 @@ response: record(string(), unknown()) }), object({
20671
20858
  userId: string().nullable()
20672
20859
  }), {
20673
20860
  kind: "mutation",
20674
- access: "view"
20861
+ access: "view",
20862
+ auth: "admin"
20675
20863
  }), method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }), method(object({
20676
20864
  userId: string(),
20677
20865
  credentialId: string()
@@ -20843,7 +21031,19 @@ var VectorStatsResultSchema = object({
20843
21031
  /** False when the backend ranks approximately. */
20844
21032
  exact: boolean()
20845
21033
  });
20846
- 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);
21034
+ method(VectorDeclareIndexInputSchema, _void(), {
21035
+ kind: "mutation",
21036
+ auth: "admin"
21037
+ }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
21038
+ kind: "mutation",
21039
+ auth: "admin"
21040
+ }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
21041
+ kind: "mutation",
21042
+ auth: "admin"
21043
+ }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
21044
+ kind: "mutation",
21045
+ auth: "admin"
21046
+ }), method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" });
20847
21047
  var ClipSchema = object({
20848
21048
  /** Opaque, provider-namespaced id. The default provider encodes the time
20849
21049
  * window so `getClipPlayback` is self-contained (no event re-query). */
@@ -22586,7 +22786,27 @@ var MediaFileLiteSchema$1 = object({
22586
22786
  sizeBytes: number(),
22587
22787
  timestamp: number()
22588
22788
  });
22589
- method(_void(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
22789
+ method(object({
22790
+ /**
22791
+ * Inline {@link IdentitySchema.coverBase64} on every row.
22792
+ *
22793
+ * Default `false`, the same inversion `listRecentFaces` and
22794
+ * `listPlates` took on 2026-08-25 (see `include-crops-default.ts` for
22795
+ * why the burden belongs on the caller that WANTS the bytes). Measured
22796
+ * on the live hub the same day: four identities cost 40,979 B with the
22797
+ * covers inline, ~10 KB of base64 per row, on a query this UI mounts
22798
+ * four times and the viewer holds at `staleTime: 30_000`.
22799
+ *
22800
+ * Nothing loses its avatar: `coverMediaKey` is already on every row and
22801
+ * the `event-media` plane serves that key `immutable` with an ETag.
22802
+ *
22803
+ * **This is an INPUT field, so it does not reach the addon until the
22804
+ * next train** — the hub router validates cap inputs against its own
22805
+ * compiled Zod and strips a key it does not know. Until then the
22806
+ * provider sees `undefined`, which resolves to `false`: the cheap shape
22807
+ * is what ships, and the opt-in becomes reachable when the train lands.
22808
+ */
22809
+ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
22590
22810
  kind: "mutation",
22591
22811
  auth: "admin"
22592
22812
  }), method(object({
@@ -24759,7 +24979,14 @@ var PlateInfoSchema = object({
24759
24979
  plateBbox: BoundingBoxSchema.optional(),
24760
24980
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
24761
24981
  keyFrameMediaKey: string().optional(),
24762
- base64: string().optional()
24982
+ base64: string().optional(),
24983
+ /**
24984
+ * Same crop as a data-plane URL, always present when the plate has a stored
24985
+ * crop. `getPlateByTrack` returns this and never inlines JPEG; `listPlates`
24986
+ * and `searchPlates` inline the crop only when their `includeCrops` input is
24987
+ * left at its `true` default.
24988
+ */
24989
+ cropUrl: string().optional()
24763
24990
  });
24764
24991
  var MediaFileLiteSchema = object({
24765
24992
  key: string(),
@@ -24777,14 +25004,34 @@ var PlateClusterSchema = object({
24777
25004
  });
24778
25005
  method(object({
24779
25006
  deviceId: number().int().optional(),
24780
- limit: number().int().positive().optional()
25007
+ limit: number().int().positive().optional(),
25008
+ /**
25009
+ * Inline the base64 crop on every row. Default `true` — the existing
25010
+ * behaviour, kept so no caller breaks.
25011
+ *
25012
+ * Set `false` once the caller renders {@link PlateInfo.cropUrl}.
25013
+ * Measured on the live hub at the 500 rows the Plates view asks for:
25014
+ * 879,403 B and 27.7 s with the crops inline, against a few KiB of
25015
+ * metadata without them — and the browser then caches the images.
25016
+ *
25017
+ * This is the plate twin of `faceGallery.listRecentFaces`'s option;
25018
+ * plates were the one gallery list left without it.
25019
+ *
25020
+ * **This is an INPUT field, so it does not reach the addon until the
25021
+ * next train.** The hub router validates cap inputs against its own
25022
+ * compiled Zod and strips a key it does not know. Until the train
25023
+ * ships, sending `false` is harmless and keeps the crops inline.
25024
+ */
25025
+ includeCrops: boolean().optional()
24781
25026
  }).optional(), array(PlateInfoSchema).readonly()), method(object({
24782
25027
  deviceId: number().int(),
24783
25028
  trackId: string()
24784
25029
  }), PlateInfoSchema.nullable()), method(object({ plateId: string() }), array(MediaFileLiteSchema).readonly()), method(object({
24785
25030
  text: string().min(1),
24786
25031
  maxDistance: number().int().min(0).optional(),
24787
- limit: number().int().positive().optional()
25032
+ limit: number().int().positive().optional(),
25033
+ /** See `listPlates.includeCrops`. Default `true`. */
25034
+ includeCrops: boolean().optional()
24788
25035
  }), array(PlateInfoSchema).readonly()), method(object({
24789
25036
  maxDistance: number().int().min(0).optional(),
24790
25037
  minClusterSize: number().int().min(2).optional(),
@@ -24798,7 +25045,13 @@ method(object({
24798
25045
  }), method(object({ plateId: string() }), _void(), {
24799
25046
  kind: "mutation",
24800
25047
  auth: "admin"
24801
- }), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
25048
+ }), method(object({
25049
+ /** Inline {@link VehicleSchema.coverBase64} on every row. Default
25050
+ * `false` — the vehicle twin of `faceGallery.listIdentities`'s
25051
+ * option; `coverMediaKey` + the `event-media` plane carry the picture.
25052
+ * INPUT field: stripped by the hub router until the train ships, which
25053
+ * resolves to `false` and is exactly the intended default. */
25054
+ includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
24802
25055
  kind: "mutation",
24803
25056
  auth: "admin"
24804
25057
  }), method(object({
@@ -28424,6 +28677,12 @@ Object.freeze({
28424
28677
  addonId: null,
28425
28678
  access: "view"
28426
28679
  },
28680
+ "deviceManager.getBindingsBatch": {
28681
+ capName: "device-manager",
28682
+ capScope: "system",
28683
+ addonId: null,
28684
+ access: "view"
28685
+ },
28427
28686
  "deviceManager.getChildren": {
28428
28687
  capName: "device-manager",
28429
28688
  capScope: "system",
@@ -28484,6 +28743,12 @@ Object.freeze({
28484
28743
  addonId: null,
28485
28744
  access: "view"
28486
28745
  },
28746
+ "deviceManager.getLinkedDevicesBatch": {
28747
+ capName: "device-manager",
28748
+ capScope: "system",
28749
+ addonId: null,
28750
+ access: "view"
28751
+ },
28487
28752
  "deviceManager.getRoleDisplayDefaults": {
28488
28753
  capName: "device-manager",
28489
28754
  capScope: "system",
@@ -30188,6 +30453,12 @@ Object.freeze({
30188
30453
  addonId: null,
30189
30454
  access: "create"
30190
30455
  },
30456
+ "pipelineAnalytics.cancelRelocateMedia": {
30457
+ capName: "pipeline-analytics",
30458
+ capScope: "device",
30459
+ addonId: null,
30460
+ access: "create"
30461
+ },
30191
30462
  "pipelineAnalytics.cancelStorageMigrationMove": {
30192
30463
  capName: "pipeline-analytics",
30193
30464
  capScope: "device",
@@ -30254,6 +30525,12 @@ Object.freeze({
30254
30525
  addonId: null,
30255
30526
  access: "view"
30256
30527
  },
30528
+ "pipelineAnalytics.getGroup": {
30529
+ capName: "pipeline-analytics",
30530
+ capScope: "device",
30531
+ addonId: null,
30532
+ access: "view"
30533
+ },
30257
30534
  "pipelineAnalytics.getKeyEvents": {
30258
30535
  capName: "pipeline-analytics",
30259
30536
  capScope: "device",
@@ -30338,6 +30615,12 @@ Object.freeze({
30338
30615
  addonId: null,
30339
30616
  access: "view"
30340
30617
  },
30618
+ "pipelineAnalytics.listGroups": {
30619
+ capName: "pipeline-analytics",
30620
+ capScope: "device",
30621
+ addonId: null,
30622
+ access: "view"
30623
+ },
30341
30624
  "pipelineAnalytics.listOpsLog": {
30342
30625
  capName: "pipeline-analytics",
30343
30626
  capScope: "device",
@@ -30350,6 +30633,12 @@ Object.freeze({
30350
30633
  addonId: null,
30351
30634
  access: "view"
30352
30635
  },
30636
+ "pipelineAnalytics.listRelocateMediaJobs": {
30637
+ capName: "pipeline-analytics",
30638
+ capScope: "device",
30639
+ addonId: null,
30640
+ access: "view"
30641
+ },
30353
30642
  "pipelineAnalytics.listRetrainAnnotations": {
30354
30643
  capName: "pipeline-analytics",
30355
30644
  capScope: "device",
@@ -30428,6 +30717,12 @@ Object.freeze({
30428
30717
  addonId: null,
30429
30718
  access: "create"
30430
30719
  },
30720
+ "pipelineAnalytics.relocateMedia": {
30721
+ capName: "pipeline-analytics",
30722
+ capScope: "device",
30723
+ addonId: null,
30724
+ access: "create"
30725
+ },
30431
30726
  "pipelineAnalytics.restageRetrainTrack": {
30432
30727
  capName: "pipeline-analytics",
30433
30728
  capScope: "device",
@@ -33119,6 +33414,11 @@ Object.freeze({
33119
33414
  form: "single",
33120
33415
  optional: false
33121
33416
  }],
33417
+ "deviceManager.getBindingsBatch": [{
33418
+ name: "deviceIds",
33419
+ form: "array",
33420
+ optional: false
33421
+ }],
33122
33422
  "deviceManager.getChildren": [{
33123
33423
  name: "parentDeviceId",
33124
33424
  form: "single",
@@ -33164,6 +33464,11 @@ Object.freeze({
33164
33464
  form: "single",
33165
33465
  optional: false
33166
33466
  }],
33467
+ "deviceManager.getLinkedDevicesBatch": [{
33468
+ name: "deviceIds",
33469
+ form: "array",
33470
+ optional: false
33471
+ }],
33167
33472
  "deviceManager.getSettingsSchema": [{
33168
33473
  name: "deviceId",
33169
33474
  form: "single",
@@ -33184,6 +33489,11 @@ Object.freeze({
33184
33489
  form: "single",
33185
33490
  optional: false
33186
33491
  }],
33492
+ "deviceManager.listAll": [{
33493
+ name: "deviceIds",
33494
+ form: "array",
33495
+ optional: true
33496
+ }],
33187
33497
  "deviceManager.loadConfig": [{
33188
33498
  name: "deviceId",
33189
33499
  form: "single",
@@ -33757,6 +34067,11 @@ Object.freeze({
33757
34067
  form: "single",
33758
34068
  optional: false
33759
34069
  }],
34070
+ "pipelineAnalytics.getGroup": [{
34071
+ name: "deviceId",
34072
+ form: "single",
34073
+ optional: false
34074
+ }],
33760
34075
  "pipelineAnalytics.getKeyEvents": [{
33761
34076
  name: "deviceId",
33762
34077
  form: "single",
@@ -33812,6 +34127,11 @@ Object.freeze({
33812
34127
  form: "array",
33813
34128
  optional: false
33814
34129
  }],
34130
+ "pipelineAnalytics.listGroups": [{
34131
+ name: "deviceIds",
34132
+ form: "array",
34133
+ optional: false
34134
+ }],
33815
34135
  "pipelineAnalytics.listOpsLog": [{
33816
34136
  name: "deviceId",
33817
34137
  form: "single",