@camstack/addon-provider-reolink 1.2.49 → 1.2.51

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.
Files changed (3) hide show
  1. package/dist/addon.js +431 -111
  2. package/dist/addon.mjs +431 -111
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -5828,6 +5828,13 @@ var BaseAddon = class {
5828
5828
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5829
5829
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5830
5830
  _registeredCapNames = [];
5831
+ /**
5832
+ * True only after `readAddonStore` actually answered. Constructor
5833
+ * defaults look like stored config when the store is down — a forked
5834
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5835
+ * mode, 2026-08-25) is not "the operator chose this".
5836
+ */
5837
+ settingsStoreReady = false;
5831
5838
  /** Default config values. Provided via constructor. */
5832
5839
  defaults;
5833
5840
  constructor(defaults) {
@@ -6228,7 +6235,9 @@ var BaseAddon = class {
6228
6235
  ];
6229
6236
  let lastErr;
6230
6237
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6231
- return await settings.readAddonStore() ?? {};
6238
+ const stored = await settings.readAddonStore() ?? {};
6239
+ this.settingsStoreReady = true;
6240
+ return stored;
6232
6241
  } catch (err) {
6233
6242
  lastErr = err;
6234
6243
  const msg = err instanceof Error ? err.message : String(err);
@@ -6236,6 +6245,7 @@ var BaseAddon = class {
6236
6245
  if (attempt === delaysMs.length) break;
6237
6246
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6238
6247
  }
6248
+ this.settingsStoreReady = false;
6239
6249
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6240
6250
  return {};
6241
6251
  }
@@ -6647,7 +6657,7 @@ function method(input, output, options) {
6647
6657
  input,
6648
6658
  output,
6649
6659
  kind: options?.kind ?? "query",
6650
- auth: options?.auth ?? "protected",
6660
+ ...options?.auth !== void 0 ? { auth: options.auth } : {},
6651
6661
  ...options?.access !== void 0 ? { access: options.access } : {},
6652
6662
  ...options?.caller !== void 0 ? { caller: options.caller } : {},
6653
6663
  timeoutMs: options?.timeoutMs
@@ -6671,7 +6681,7 @@ function event(data) {
6671
6681
  }
6672
6682
  var StaticDirOutputSchema$1 = object({ staticDir: string() });
6673
6683
  var VersionOutputSchema$1 = object({ version: string() });
6674
- method(_void(), StaticDirOutputSchema$1), method(_void(), VersionOutputSchema$1);
6684
+ method(_void(), StaticDirOutputSchema$1, { auth: "admin" }), method(_void(), VersionOutputSchema$1, { auth: "admin" });
6675
6685
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
6676
6686
  DeviceType["Camera"] = "camera";
6677
6687
  DeviceType["Hub"] = "hub";
@@ -6992,7 +7002,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
6992
7002
  }({});
6993
7003
  var StaticDirOutputSchema = object({ staticDir: string() });
6994
7004
  var VersionOutputSchema = object({ version: string() });
6995
- method(_void(), StaticDirOutputSchema), method(_void(), VersionOutputSchema);
7005
+ method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
6996
7006
  /**
6997
7007
  * device-ops — device-scoped cap that unifies the per-IDevice operations
6998
7008
  * previously routed through the `.device-ops` Moleculer bridge service.
@@ -7630,24 +7640,6 @@ var RecordingRetentionSchema = object({
7630
7640
  maxSizeGb: number().min(0).optional()
7631
7641
  });
7632
7642
  /**
7633
- * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7634
- * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7635
- * previews at. Five graduated steps; absent on a config = `standard` (the
7636
- * shipped default, matching `sheet-geometry`/`sheet-composer`).
7637
- *
7638
- * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7639
- * Each window's index sidecar carries its own tile dims, so a camera whose
7640
- * preset changed over time renders every historical window at the dims it was
7641
- * written with.
7642
- */
7643
- var ScrubThumbnailPresetSchema = _enum([
7644
- "minimal",
7645
- "low",
7646
- "standard",
7647
- "high",
7648
- "max"
7649
- ]);
7650
- /**
7651
7643
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7652
7644
  *
7653
7645
  * `bands` is the ONLY authored recording intent: what to record, when, and on
@@ -7655,7 +7647,11 @@ var ScrubThumbnailPresetSchema = _enum([
7655
7647
  * other field is a storage knob (profiles, segment length, retention, scrub).
7656
7648
  *
7657
7649
  * STRICT on purpose: the legacy authoring surface (`schedule`/`schedules`/
7658
- * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30.
7650
+ * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30,
7651
+ * and `scrubThumbnails` — a five-step fidelity knob for a sprite tier that was
7652
+ * deleted on 2026-07-24 and had ZERO consumers in the recorder — on 2026-08-25
7653
+ * (D62: a switch that writes a store nobody reads is worse than no switch).
7654
+ * Stored rows keep loading: the READ schema is `.strip()` (config-store.ts).
7659
7655
  * A stale caller must fail loudly — silently stripping its legacy intent would
7660
7656
  * persist a band-less config, i.e. silently stop recording the camera.
7661
7657
  */
@@ -7678,14 +7674,7 @@ var RecordingConfigSchema = object({
7678
7674
  * "off" is the absence of a covering band, never a band value.
7679
7675
  */
7680
7676
  bands: array(RecordingBandSchema).default([]),
7681
- retention: RecordingRetentionSchema.optional(),
7682
- /**
7683
- * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7684
- * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7685
- * windows only — existing sheets are immutable, and each window's index
7686
- * carries its own tile dims so mixed-preset history renders correctly.
7687
- */
7688
- scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7677
+ retention: RecordingRetentionSchema.optional()
7689
7678
  }).strict();
7690
7679
  /**
7691
7680
  * Entity-relocation job state (storage entity-routing spec, Phase 4).
@@ -7761,10 +7750,11 @@ var RelocateFootageInputSchema = object({
7761
7750
  * `RecordingConfig.enabled` or camera wrapper bindings. */
7762
7751
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
7763
7752
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
7764
- var StorageMigrationMediaMoveInputSchema = object({
7753
+ var RelocateMediaInputSchema = object({
7765
7754
  toLocationId: string(),
7766
7755
  throttleMbps: number().min(1).max(1e3).optional()
7767
- }).extend({ leaseId: string().min(1) });
7756
+ });
7757
+ var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
7768
7758
  /** The independently selectable logical storage classes. `recordings`
7769
7759
  * encompasses the high and mid segment profiles; `recordingsLow` is low
7770
7760
  * segments; `eventMedia` is post-analysis blobs. */
@@ -8234,7 +8224,26 @@ var LabelDefinitionSchema = object({
8234
8224
  description: string().optional(),
8235
8225
  icon: string().optional()
8236
8226
  });
8237
- var ClassMapDefinitionSchema = object({
8227
+ /**
8228
+ * Wire schema for a per-model CATALOG classMap override
8229
+ * (`ModelCatalogEntry.classMap` / `ModelConvertMetadata.classMap`) —
8230
+ * restricted to {@link CLASS_MAP_MACRO_TARGETS}, the only macros the
8231
+ * detection pipeline executor actually routes.
8232
+ *
8233
+ * This is deliberately a DIFFERENT, narrower shape than the general-purpose
8234
+ * `ClassMapDefinition` interface above (e.g. `IDetectionAddon.getClassMap()`
8235
+ * and the audio `YAMNET_TO_MACRO` catalog both use macro targets outside this
8236
+ * enum) — the two used to share the name `ClassMapDefinition`/
8237
+ * `ClassMapDefinitionSchema`, which made the schema-type-twin guard
8238
+ * (`scripts/check-schema-type-twins.ts`) flag them as a duplicated shape. They
8239
+ * are not: it is two different concepts colliding on a name. Keep this type
8240
+ * under its own name rather than reusing `ClassMapDefinition` — reusing it
8241
+ * would either narrow every `ClassMapDefinition` consumer to the four
8242
+ * detection macros (breaking `YAMNET_TO_MACRO`) or drop the validation this
8243
+ * schema exists for (see the "rejects a classMap whose target is not a
8244
+ * detection macro" test in `model-catalog-schema.test.ts`).
8245
+ */
8246
+ var DetectionCatalogClassMapSchema = object({
8238
8247
  mapping: record(string(), _enum([
8239
8248
  "person",
8240
8249
  "vehicle",
@@ -8326,6 +8335,12 @@ var ModelVariantGroupSchema = object({
8326
8335
  */
8327
8336
  resolution: number().int().positive().optional()
8328
8337
  });
8338
+ var ModelProviderIdSchema = _enum([
8339
+ "camstack",
8340
+ "frigate",
8341
+ "scrypted",
8342
+ "custom"
8343
+ ]);
8329
8344
  var ModelCatalogEntrySchema = object({
8330
8345
  id: string(),
8331
8346
  name: string(),
@@ -8423,11 +8438,17 @@ var ModelCatalogEntrySchema = object({
8423
8438
  */
8424
8439
  group: ModelVariantGroupSchema.optional(),
8425
8440
  /**
8441
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8442
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8443
+ * persisted before this field existed (`inferModelProvider` fills those).
8444
+ */
8445
+ provider: ModelProviderIdSchema.optional(),
8446
+ /**
8426
8447
  * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8427
8448
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8428
8449
  * labels already ARE the CamStack macros (Scrypted identity map).
8429
8450
  */
8430
- classMap: ClassMapDefinitionSchema.optional()
8451
+ classMap: DetectionCatalogClassMapSchema.optional()
8431
8452
  });
8432
8453
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8433
8454
  format: literal("openvino"),
@@ -8457,7 +8478,7 @@ var ModelConvertMetadataSchema = object({
8457
8478
  "segmentation"
8458
8479
  ]),
8459
8480
  faceAlignment: boolean().optional(),
8460
- classMap: ClassMapDefinitionSchema.optional()
8481
+ classMap: DetectionCatalogClassMapSchema.optional()
8461
8482
  });
8462
8483
  var ConvertResultSchema = object({
8463
8484
  entry: ModelCatalogEntrySchema,
@@ -9320,7 +9341,7 @@ var AddonPageDeclarationSchema = object({
9320
9341
  /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
9321
9342
  sectionLabel: string().optional()
9322
9343
  });
9323
- method(_void(), array(AddonPageDeclarationSchema).readonly());
9344
+ method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
9324
9345
  var AddonHttpRouteSchema = object({
9325
9346
  method: _enum([
9326
9347
  "GET",
@@ -9555,7 +9576,7 @@ var WidgetMetadataSchema = object({
9555
9576
  defaultColumns: number().int().min(1).max(12).default(6),
9556
9577
  defaultRows: number().int().min(1).max(12).default(1)
9557
9578
  });
9558
- method(_void(), array(WidgetMetadataSchema).readonly());
9579
+ method(_void(), array(WidgetMetadataSchema).readonly(), { auth: "admin" });
9559
9580
  /**
9560
9581
  * `addon-widgets` — system-scoped singleton aggregator cap. Public-facing
9561
9582
  * surface that admin-ui consumes through `useAddonWidgetsListWidgets()`.
@@ -11191,7 +11212,7 @@ var CustomModelDescriptorSchema = object({
11191
11212
  stepId: string(),
11192
11213
  entry: ModelCatalogEntrySchema
11193
11214
  });
11194
- method(_void(), array(CustomModelDescriptorSchema).readonly());
11215
+ method(_void(), array(CustomModelDescriptorSchema).readonly(), { auth: "admin" });
11195
11216
  /**
11196
11217
  * Query filter for settings-store collections.
11197
11218
  */
@@ -11278,7 +11299,8 @@ method(object({
11278
11299
  }), _void(), { kind: "mutation" }), method(object({
11279
11300
  namespace: string().optional(),
11280
11301
  collection: string(),
11281
- filter: QueryFilterSchema.optional()
11302
+ filter: QueryFilterSchema.optional(),
11303
+ columns: array(string()).readonly().optional()
11282
11304
  }), array(SettingsRecordSchema).readonly()), method(object({
11283
11305
  namespace: string().optional(),
11284
11306
  collection: string(),
@@ -11341,46 +11363,87 @@ var EngineInfoSchema = object({
11341
11363
  kind: _enum(["relational", "vector"]),
11342
11364
  displayName: string()
11343
11365
  });
11344
- method(_void(), EngineInfoSchema), method(object({
11366
+ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
11345
11367
  namespace: string().optional(),
11346
11368
  collection: string(),
11347
11369
  key: string()
11348
- }), unknown()), method(object({
11370
+ }), unknown(), { auth: "admin" }), method(object({
11349
11371
  namespace: string().optional(),
11350
11372
  collection: string(),
11351
11373
  key: string(),
11352
11374
  value: unknown()
11353
- }), _void(), { kind: "mutation" }), method(object({
11375
+ }), _void(), {
11376
+ kind: "mutation",
11377
+ auth: "admin"
11378
+ }), method(object({
11354
11379
  namespace: string().optional(),
11355
11380
  collection: string(),
11356
- filter: QueryFilterSchema.optional()
11357
- }), array(SettingsRecordSchema).readonly()), method(object({
11381
+ filter: QueryFilterSchema.optional(),
11382
+ /**
11383
+ * SQL-level column projection — MUST mirror `settings-store.query`.
11384
+ *
11385
+ * ⚠ An earlier version of this comment blamed Zod stripping, and that
11386
+ * was wrong — corrected 2026-08-26 after the hop map
11387
+ * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
11388
+ * There is **no Zod parse at all** between the door and the engine: the
11389
+ * dispatcher forwards the payload verbatim and UDS carries it whole. A
11390
+ * field declared here reaches `SqliteSettingsBackend` either way.
11391
+ *
11392
+ * What actually lost `columns` was the THIRD declaration of this shape:
11393
+ * `SettingsQueryInput` in `interfaces/storage.ts`, a hand-written TS
11394
+ * interface the engine destructures from. The field existed on both
11395
+ * schemas and the engine still never read it, because nothing checks a
11396
+ * registered provider against `InferProvider<cap>` —
11397
+ * `ProviderRegistration.provider` is typed `object`.
11398
+ *
11399
+ * It is declared here anyway, and must stay in step with
11400
+ * `settings-store.query`: a caller reading only the cap definitions has
11401
+ * to be able to see that this call carries a projection.
11402
+ * `data-door-schema-parity.spec.ts` keeps the two aligned.
11403
+ */
11404
+ columns: array(string()).readonly().optional()
11405
+ }), array(SettingsRecordSchema).readonly(), { auth: "admin" }), method(object({
11358
11406
  namespace: string().optional(),
11359
11407
  collection: string(),
11360
11408
  record: SettingsRecordSchema
11361
- }), _void(), { kind: "mutation" }), method(object({
11409
+ }), _void(), {
11410
+ kind: "mutation",
11411
+ auth: "admin"
11412
+ }), method(object({
11362
11413
  namespace: string().optional(),
11363
11414
  collection: string(),
11364
11415
  id: string(),
11365
11416
  data: record(string(), unknown())
11366
- }), _void(), { kind: "mutation" }), method(object({
11417
+ }), _void(), {
11418
+ kind: "mutation",
11419
+ auth: "admin"
11420
+ }), method(object({
11367
11421
  namespace: string().optional(),
11368
11422
  collection: string(),
11369
11423
  key: string()
11370
- }), _void(), { kind: "mutation" }), method(object({
11424
+ }), _void(), {
11425
+ kind: "mutation",
11426
+ auth: "admin"
11427
+ }), method(object({
11371
11428
  namespace: string().optional(),
11372
11429
  collection: string(),
11373
11430
  filter: MutationFilterSchema
11374
- }), object({ deleted: number().int() }), { kind: "mutation" }), method(object({
11431
+ }), object({ deleted: number().int() }), {
11432
+ kind: "mutation",
11433
+ auth: "admin"
11434
+ }), method(object({
11375
11435
  namespace: string().optional(),
11376
11436
  collection: string(),
11377
11437
  filter: MutationFilterSchema,
11378
11438
  data: record(string(), unknown())
11379
- }), object({ updated: number().int() }), { kind: "mutation" }), method(object({
11439
+ }), object({ updated: number().int() }), {
11440
+ kind: "mutation",
11441
+ auth: "admin"
11442
+ }), method(object({
11380
11443
  namespace: string().optional(),
11381
11444
  collection: string(),
11382
11445
  filter: QueryFilterSchema.optional()
11383
- }), number()), method(object({
11446
+ }), number(), { auth: "admin" }), method(object({
11384
11447
  namespace: string().optional(),
11385
11448
  collection: string(),
11386
11449
  field: string(),
@@ -11390,15 +11453,18 @@ method(_void(), EngineInfoSchema), method(object({
11390
11453
  }), array(object({
11391
11454
  bucket: number().int(),
11392
11455
  count: number().int()
11393
- })).readonly()), method(object({
11456
+ })).readonly(), { auth: "admin" }), method(object({
11394
11457
  namespace: string().optional(),
11395
11458
  collection: string()
11396
- }), boolean()), method(object({
11459
+ }), boolean(), { auth: "admin" }), method(object({
11397
11460
  namespace: string().optional(),
11398
11461
  collection: string(),
11399
11462
  columns: array(CollectionColumnSchema).readonly(),
11400
11463
  indexes: array(CollectionIndexSchema).readonly().optional()
11401
- }), _void(), { kind: "mutation" });
11464
+ }), _void(), {
11465
+ kind: "mutation",
11466
+ auth: "admin"
11467
+ });
11402
11468
  /**
11403
11469
  * shm ring usage stats for a `frameSink: 'shm'` decoder session —
11404
11470
  * exposed via `decoder.getShmStats` so downstream consumers can
@@ -12226,6 +12292,27 @@ var LinkedDeviceSchema = object({
12226
12292
  features: array(string()),
12227
12293
  producesTrackedEvents: boolean().optional()
12228
12294
  });
12295
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
12296
+ * The batch answer needs the tag; the single-device answer already has it
12297
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
12298
+ var LinkedDevicesForDeviceSchema = object({
12299
+ deviceId: number(),
12300
+ mode: LinkedDevicesModeSchema,
12301
+ devices: array(LinkedDeviceSchema)
12302
+ });
12303
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
12304
+ * `getAllBindings` all answer in. Declared once: three copies of the same
12305
+ * object literal is exactly how the three drift apart. */
12306
+ var DeviceBindingsForDeviceSchema = object({
12307
+ deviceId: number(),
12308
+ entries: array(object({
12309
+ capName: string(),
12310
+ kind: _enum(["native", "wrapped"]),
12311
+ providerAddonId: string(),
12312
+ providerNodeId: string(),
12313
+ nativeAddonId: string()
12314
+ }))
12315
+ });
12229
12316
  var SavedDeviceRowSchema = object({
12230
12317
  /** Numeric id reserved at allocateDeviceId time. */
12231
12318
  id: number(),
@@ -12451,11 +12538,25 @@ method(object({
12451
12538
  projection: _enum(["full", "slim"]).optional(),
12452
12539
  /** Return only camera devices. Filtering server-side instead of
12453
12540
  * shipping 293 rows to find 12. */
12454
- isCamera: boolean().optional()
12541
+ isCamera: boolean().optional(),
12542
+ /**
12543
+ * Return only these device ids. For the caller that already KNOWS the
12544
+ * handful it wants and needs a field the id-bearing answer does not
12545
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12546
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12547
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12548
+ * refetches on the reconcile interval, on a phone.
12549
+ *
12550
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12551
+ * keys rather than rejecting them (verified against the live hub
12552
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12553
+ * it answers today and the caller filters as it already does.
12554
+ */
12555
+ deviceIds: array(number()).optional()
12455
12556
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12456
12557
  mode: LinkedDevicesModeSchema,
12457
12558
  devices: array(LinkedDeviceSchema)
12458
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12559
+ })), 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({
12459
12560
  deviceId: number(),
12460
12561
  values: record(string(), unknown())
12461
12562
  }), object({ success: literal(true) }), {
@@ -12482,25 +12583,7 @@ method(object({
12482
12583
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
12483
12584
  kind: "mutation",
12484
12585
  auth: "admin"
12485
- }), method(object({ deviceId: number() }), object({
12486
- deviceId: number(),
12487
- entries: array(object({
12488
- capName: string(),
12489
- kind: _enum(["native", "wrapped"]),
12490
- providerAddonId: string(),
12491
- providerNodeId: string(),
12492
- nativeAddonId: string()
12493
- }))
12494
- })), method(object({}), array(object({
12495
- deviceId: number(),
12496
- entries: array(object({
12497
- capName: string(),
12498
- kind: _enum(["native", "wrapped"]),
12499
- providerAddonId: string(),
12500
- providerNodeId: string(),
12501
- nativeAddonId: string()
12502
- }))
12503
- }))), method(object({
12586
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12504
12587
  deviceId: number(),
12505
12588
  capName: string(),
12506
12589
  wrapperAddonId: string(),
@@ -12671,7 +12754,7 @@ method(object({
12671
12754
  crop: _instanceof(Uint8Array),
12672
12755
  width: number(),
12673
12756
  height: number()
12674
- }), EmbeddingResultSchema), method(object({ text: string() }), EmbeddingResultSchema), method(_void(), EmbeddingInfoSchema);
12757
+ }), EmbeddingResultSchema, { auth: "admin" }), method(object({ text: string() }), EmbeddingResultSchema, { auth: "admin" }), method(_void(), EmbeddingInfoSchema, { auth: "admin" });
12675
12758
  /**
12676
12759
  * filesystem-browse — per-node capability for browsing the node's local
12677
12760
  * filesystem. Reads are unconfined (whole filesystem, from `/` down); WRITES
@@ -12964,19 +13047,22 @@ method(LlmGenerateBaseInputSchema.extend({
12964
13047
  runtime: ManagedRuntimeConfigSchema,
12965
13048
  /** The managed profile's timeout, threaded by the hub provider. */
12966
13049
  timeoutMs: number().int().positive().optional()
12967
- }), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
13050
+ }), LlmGenerateResultSchema, {
13051
+ kind: "mutation",
13052
+ auth: "admin"
13053
+ }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
12968
13054
  kind: "mutation",
12969
13055
  auth: "admin"
12970
13056
  }), method(object({}), _void(), {
12971
13057
  kind: "mutation",
12972
13058
  auth: "admin"
12973
- }), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
13059
+ }), method(object({}), LlmRuntimeStatusSchema, { auth: "admin" }), method(object({ model: ManagedModelRefSchema }), _void(), {
12974
13060
  kind: "mutation",
12975
13061
  auth: "admin"
12976
13062
  }), method(object({ file: string() }), _void(), {
12977
13063
  kind: "mutation",
12978
13064
  auth: "admin"
12979
- }), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
13065
+ }), method(object({}), array(LlmNodeModelSchema), { auth: "admin" }), method(object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" });
12980
13066
  /**
12981
13067
  * `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
12982
13068
  * methods concat-fan across providers; single-row methods route to ONE
@@ -14910,12 +14996,15 @@ var NcOccupancyConditionSchema = object({
14910
14996
  * there is no second switch that can disagree with the first and every rule
14911
14997
  * authored before the decision migrates for free (`audioModeOf`):
14912
14998
  *
14913
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14914
- * classifier labels with one of them. No window, no percentage:
14915
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14916
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14917
- * the analyzer's (`classificationMinScore`, per device) a label only
14918
- * reaches this condition if the classifier was already confident enough.
14999
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
15000
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
15001
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
15002
+ * frames is the wrong question for a classifier that labels 1–3 frames
15003
+ * per episode. The count window is the brake that drops a single-frame
15004
+ * false positive; the rule's own `throttle` cooldown is the other. The
15005
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
15006
+ * per device) — a label only reaches this condition if the classifier was
15007
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14919
15008
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14920
15009
  * the condition: at least `hitPercent`% of the samples over
14921
15010
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14942,14 +15031,22 @@ var NcOccupancyConditionSchema = object({
14942
15031
  * an operator who typed `dog` mean the same thing.
14943
15032
  */
14944
15033
  var NcAudioConditionSchema = object({
14945
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
15034
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14946
15035
  labels: array(string().min(1)).min(1).optional(),
14947
15036
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14948
15037
  dbThreshold: number().min(-96).max(0).optional(),
14949
15038
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14950
15039
  hitPercent: number().int().min(1).max(100).default(60),
14951
15040
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14952
- samplingSeconds: number().int().min(1).max(300).default(10)
15041
+ samplingSeconds: number().int().min(1).max(300).default(10),
15042
+ /**
15043
+ * LABEL MODE: how many labelled frames must land inside
15044
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
15045
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
15046
+ */
15047
+ confirmHits: number().int().min(1).max(20).optional(),
15048
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
15049
+ confirmWindowSec: number().int().min(1).max(60).optional()
14953
15050
  });
14954
15051
  /**
14955
15052
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -16476,7 +16573,7 @@ var OauthIntegrationDescriptorSchema = object({
16476
16573
  */
16477
16574
  refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
16478
16575
  });
16479
- method(_void(), OauthIntegrationDescriptorSchema);
16576
+ method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
16480
16577
  /**
16481
16578
  * pipeline-analytics — device-scoped wrapper cap. Refines raw
16482
16579
  * per-frame detections emitted by the pipeline runner into tracked
@@ -17323,6 +17420,46 @@ var RecentTracksPageSchema = object({
17323
17420
  /** Cursor for the next page, or null when this page is the last. */
17324
17421
  nextCursor: string().nullable()
17325
17422
  });
17423
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
17424
+ var LIST_GROUPS_MAX_LIMIT = 100;
17425
+ var AnalyticsGroupRecordSchema = object({
17426
+ id: string(),
17427
+ deviceId: number().int(),
17428
+ openedAt: number().int(),
17429
+ closedAt: number().int(),
17430
+ timestamp: number().int(),
17431
+ memberCount: number().int(),
17432
+ memberTrackIds: array(string()).readonly(),
17433
+ className: string(),
17434
+ classes: array(string()).readonly(),
17435
+ /** Relative event-media path, or null when the group has no picture yet. */
17436
+ mediaUrl: string().nullable(),
17437
+ singleton: boolean()
17438
+ });
17439
+ var AnalyticsGroupMemberSchema = object({
17440
+ trackId: string(),
17441
+ deviceId: number().int(),
17442
+ className: string(),
17443
+ firstSeen: number().int(),
17444
+ lastSeen: number().int(),
17445
+ mediaUrl: string().nullable()
17446
+ });
17447
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
17448
+ var ListGroupsQueryInput = object({
17449
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
17450
+ deviceIds: array(number()),
17451
+ /** Window lower bound on `closedAt` (inclusive). */
17452
+ since: number().optional(),
17453
+ /** Window upper bound on `openedAt` (inclusive). */
17454
+ until: number().optional(),
17455
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
17456
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
17457
+ cursor: string().optional()
17458
+ });
17459
+ var ListGroupsPageSchema = object({
17460
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
17461
+ nextCursor: string().nullable()
17462
+ });
17326
17463
  var KeyEventQueryInput = object({
17327
17464
  deviceId: number(),
17328
17465
  /** Window lower bound (track firstSeen ≥ since). */
@@ -17398,7 +17535,9 @@ var TrackCascadeCountsSchema = object({
17398
17535
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
17399
17536
  plates: number().int(),
17400
17537
  /** Per-track CLIP search vectors removed (best-effort). */
17401
- embeddings: number().int()
17538
+ embeddings: number().int(),
17539
+ /** Group membership + group rows removed with their last member (best-effort). */
17540
+ groups: number().int()
17402
17541
  });
17403
17542
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
17404
17543
  var DiskReconcileCountsSchema = object({
@@ -17544,7 +17683,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17544
17683
  * stationary registry). Default false: the timeline lists passages,
17545
17684
  * not parking records (operator decision, 2026-08-15). */
17546
17685
  includeStationary: boolean().optional()
17547
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17686
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17687
+ deviceId: number(),
17688
+ groupId: string().min(1)
17689
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17548
17690
  kind: "mutation",
17549
17691
  auth: "admin"
17550
17692
  }), 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({
@@ -17626,6 +17768,15 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17626
17768
  }), method(object({ jobId: string() }), RelocateJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
17627
17769
  kind: "mutation",
17628
17770
  auth: "admin"
17771
+ }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
17772
+ kind: "mutation",
17773
+ auth: "admin"
17774
+ }), method(object({}), array(RelocateJobSchema).readonly(), {
17775
+ kind: "query",
17776
+ auth: "admin"
17777
+ }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
17778
+ kind: "mutation",
17779
+ auth: "admin"
17629
17780
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
17630
17781
  kind: "query",
17631
17782
  auth: "admin"
@@ -17780,6 +17931,11 @@ object({
17780
17931
  "jpeg"
17781
17932
  ])
17782
17933
  });
17934
+ /**
17935
+ * Process-local frame identity. It is serializable so it can ride an in-process
17936
+ * capability call, but `registryId` deliberately prevents resolution in any
17937
+ * other process or execution group.
17938
+ */
17783
17939
  var FrameRefSchema = object({
17784
17940
  registryId: string().min(1),
17785
17941
  id: string().min(1),
@@ -17854,7 +18010,8 @@ var PipelineModelOptionSchema = object({
17854
18010
  sizeMB: number()
17855
18011
  })),
17856
18012
  group: ModelVariantGroupSchema.optional(),
17857
- legacy: boolean().optional()
18013
+ legacy: boolean().optional(),
18014
+ provider: ModelProviderIdSchema.optional()
17858
18015
  });
17859
18016
  var ConfigFieldBridge = custom$2();
17860
18017
  var PipelineAddonSchemaSchema = object({
@@ -20088,7 +20245,7 @@ method(object({
20088
20245
  * linking rather than produce an eternal token.
20089
20246
  */
20090
20247
  ttlSec: union([number().int().positive(), literal("never")]).optional()
20091
- }), object({ token: string() })), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable());
20248
+ }), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
20092
20249
  var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
20093
20250
  providerId: string().min(1),
20094
20251
  displayName: string().min(1),
@@ -20183,10 +20340,13 @@ var EvictResultSchema = object({
20183
20340
  /** True when the provider has nothing left it is willing to drop on this location. */
20184
20341
  exhausted: boolean()
20185
20342
  });
20186
- method(object({ locationId: string() }), EvictableUsageSchema), method(object({
20343
+ method(object({ locationId: string() }), EvictableUsageSchema, { auth: "admin" }), method(object({
20187
20344
  locationId: string(),
20188
20345
  targetBytes: number().int().positive()
20189
- }), EvictResultSchema, { kind: "mutation" });
20346
+ }), EvictResultSchema, {
20347
+ kind: "mutation",
20348
+ auth: "admin"
20349
+ });
20190
20350
  method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin" }), method(StorageMigrationInputSchema, object({ jobId: string() }), {
20191
20351
  kind: "mutation",
20192
20352
  auth: "admin"
@@ -20246,26 +20406,50 @@ var ReadChunkInputSchema = object({
20246
20406
  length: number()
20247
20407
  });
20248
20408
  var EndDownloadInputSchema = object({ downloadId: string() });
20249
- method(_void(), ProviderInfoSchema), method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }), method(object({
20409
+ method(_void(), ProviderInfoSchema, { auth: "admin" }), method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }), method(object({
20250
20410
  location: StorageLocationSchema,
20251
20411
  relativePath: string()
20252
- }), string()), method(object({
20412
+ }), string(), { auth: "admin" }), method(object({
20253
20413
  location: StorageLocationSchema,
20254
20414
  relativePath: string(),
20255
20415
  data: _instanceof(Uint8Array)
20256
- }), _void(), { kind: "mutation" }), method(object({
20416
+ }), _void(), {
20417
+ kind: "mutation",
20418
+ auth: "admin"
20419
+ }), method(object({
20257
20420
  location: StorageLocationSchema,
20258
20421
  relativePath: string()
20259
- }), _instanceof(Uint8Array)), method(object({
20422
+ }), _instanceof(Uint8Array), { auth: "admin" }), method(object({
20260
20423
  location: StorageLocationSchema,
20261
20424
  relativePath: string()
20262
- }), boolean()), method(object({
20425
+ }), boolean(), { auth: "admin" }), method(object({
20263
20426
  location: StorageLocationSchema,
20264
20427
  prefix: string().optional()
20265
- }), array(string()).readonly()), method(object({
20428
+ }), array(string()).readonly(), { auth: "admin" }), method(object({
20266
20429
  location: StorageLocationSchema,
20267
20430
  relativePath: string()
20268
- }), _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" });
20431
+ }), _void(), {
20432
+ kind: "mutation",
20433
+ auth: "admin"
20434
+ }), method(object({ location: StorageLocationSchema }), number().nullable(), { auth: "admin" }), method(BeginUploadInputSchema, BeginUploadResultSchema, {
20435
+ kind: "mutation",
20436
+ auth: "admin"
20437
+ }), method(WriteChunkInputSchema, _void(), {
20438
+ kind: "mutation",
20439
+ auth: "admin"
20440
+ }), method(FinalizeUploadInputSchema, _void(), {
20441
+ kind: "mutation",
20442
+ auth: "admin"
20443
+ }), method(AbortUploadInputSchema, _void(), {
20444
+ kind: "mutation",
20445
+ auth: "admin"
20446
+ }), method(BeginDownloadInputSchema, BeginDownloadResultSchema, {
20447
+ kind: "mutation",
20448
+ auth: "admin"
20449
+ }), method(ReadChunkInputSchema, _instanceof(Uint8Array), { auth: "admin" }), method(EndDownloadInputSchema, _void(), {
20450
+ kind: "mutation",
20451
+ auth: "admin"
20452
+ });
20269
20453
  /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
20270
20454
  var ProfileSettingsSchemaBridge = unknown().nullable();
20271
20455
  var ProfileSettingsBagSchema = record(string(), unknown());
@@ -20523,7 +20707,8 @@ method(object({
20523
20707
  access: "create"
20524
20708
  }), method(object({ userId: string().optional() }), object({ optionsJSON: record(string(), unknown()) }), {
20525
20709
  kind: "mutation",
20526
- access: "view"
20710
+ access: "view",
20711
+ auth: "admin"
20527
20712
  }), method(object({
20528
20713
  /** Required — the user the assertion belongs to (verified). */
20529
20714
  userId: string(),
@@ -20531,10 +20716,12 @@ method(object({
20531
20716
  response: record(string(), unknown())
20532
20717
  }), object({ verified: boolean() }), {
20533
20718
  kind: "mutation",
20534
- access: "view"
20719
+ access: "view",
20720
+ auth: "admin"
20535
20721
  }), method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
20536
20722
  kind: "mutation",
20537
- access: "view"
20723
+ access: "view",
20724
+ auth: "admin"
20538
20725
  }), method(object({
20539
20726
  /** AuthenticationResponseJSON from the browser. */
20540
20727
  response: record(string(), unknown()) }), object({
@@ -20542,7 +20729,8 @@ response: record(string(), unknown()) }), object({
20542
20729
  userId: string().nullable()
20543
20730
  }), {
20544
20731
  kind: "mutation",
20545
- access: "view"
20732
+ access: "view",
20733
+ auth: "admin"
20546
20734
  }), method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }), method(object({
20547
20735
  userId: string(),
20548
20736
  credentialId: string()
@@ -20714,7 +20902,19 @@ var VectorStatsResultSchema = object({
20714
20902
  /** False when the backend ranks approximately. */
20715
20903
  exact: boolean()
20716
20904
  });
20717
- 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);
20905
+ method(VectorDeclareIndexInputSchema, _void(), {
20906
+ kind: "mutation",
20907
+ auth: "admin"
20908
+ }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
20909
+ kind: "mutation",
20910
+ auth: "admin"
20911
+ }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
20912
+ kind: "mutation",
20913
+ auth: "admin"
20914
+ }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
20915
+ kind: "mutation",
20916
+ auth: "admin"
20917
+ }), method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" });
20718
20918
  var ClipSchema = object({
20719
20919
  /** Opaque, provider-namespaced id. The default provider encodes the time
20720
20920
  * window so `getClipPlayback` is self-contained (no event re-query). */
@@ -23167,7 +23367,27 @@ var MediaFileLiteSchema$1 = object({
23167
23367
  sizeBytes: number(),
23168
23368
  timestamp: number()
23169
23369
  });
23170
- method(_void(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
23370
+ method(object({
23371
+ /**
23372
+ * Inline {@link IdentitySchema.coverBase64} on every row.
23373
+ *
23374
+ * Default `false`, the same inversion `listRecentFaces` and
23375
+ * `listPlates` took on 2026-08-25 (see `include-crops-default.ts` for
23376
+ * why the burden belongs on the caller that WANTS the bytes). Measured
23377
+ * on the live hub the same day: four identities cost 40,979 B with the
23378
+ * covers inline, ~10 KB of base64 per row, on a query this UI mounts
23379
+ * four times and the viewer holds at `staleTime: 30_000`.
23380
+ *
23381
+ * Nothing loses its avatar: `coverMediaKey` is already on every row and
23382
+ * the `event-media` plane serves that key `immutable` with an ETag.
23383
+ *
23384
+ * **This is an INPUT field, so it does not reach the addon until the
23385
+ * next train** — the hub router validates cap inputs against its own
23386
+ * compiled Zod and strips a key it does not know. Until then the
23387
+ * provider sees `undefined`, which resolves to `false`: the cheap shape
23388
+ * is what ships, and the opt-in becomes reachable when the train lands.
23389
+ */
23390
+ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
23171
23391
  kind: "mutation",
23172
23392
  auth: "admin"
23173
23393
  }), method(object({
@@ -26055,7 +26275,14 @@ var PlateInfoSchema = object({
26055
26275
  plateBbox: BoundingBoxSchema.optional(),
26056
26276
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
26057
26277
  keyFrameMediaKey: string().optional(),
26058
- base64: string().optional()
26278
+ base64: string().optional(),
26279
+ /**
26280
+ * Same crop as a data-plane URL, always present when the plate has a stored
26281
+ * crop. `getPlateByTrack` returns this and never inlines JPEG; `listPlates`
26282
+ * and `searchPlates` inline the crop only when their `includeCrops` input is
26283
+ * left at its `true` default.
26284
+ */
26285
+ cropUrl: string().optional()
26059
26286
  });
26060
26287
  var MediaFileLiteSchema = object({
26061
26288
  key: string(),
@@ -26073,14 +26300,34 @@ var PlateClusterSchema = object({
26073
26300
  });
26074
26301
  method(object({
26075
26302
  deviceId: number().int().optional(),
26076
- limit: number().int().positive().optional()
26303
+ limit: number().int().positive().optional(),
26304
+ /**
26305
+ * Inline the base64 crop on every row. Default `true` — the existing
26306
+ * behaviour, kept so no caller breaks.
26307
+ *
26308
+ * Set `false` once the caller renders {@link PlateInfo.cropUrl}.
26309
+ * Measured on the live hub at the 500 rows the Plates view asks for:
26310
+ * 879,403 B and 27.7 s with the crops inline, against a few KiB of
26311
+ * metadata without them — and the browser then caches the images.
26312
+ *
26313
+ * This is the plate twin of `faceGallery.listRecentFaces`'s option;
26314
+ * plates were the one gallery list left without it.
26315
+ *
26316
+ * **This is an INPUT field, so it does not reach the addon until the
26317
+ * next train.** The hub router validates cap inputs against its own
26318
+ * compiled Zod and strips a key it does not know. Until the train
26319
+ * ships, sending `false` is harmless and keeps the crops inline.
26320
+ */
26321
+ includeCrops: boolean().optional()
26077
26322
  }).optional(), array(PlateInfoSchema).readonly()), method(object({
26078
26323
  deviceId: number().int(),
26079
26324
  trackId: string()
26080
26325
  }), PlateInfoSchema.nullable()), method(object({ plateId: string() }), array(MediaFileLiteSchema).readonly()), method(object({
26081
26326
  text: string().min(1),
26082
26327
  maxDistance: number().int().min(0).optional(),
26083
- limit: number().int().positive().optional()
26328
+ limit: number().int().positive().optional(),
26329
+ /** See `listPlates.includeCrops`. Default `true`. */
26330
+ includeCrops: boolean().optional()
26084
26331
  }), array(PlateInfoSchema).readonly()), method(object({
26085
26332
  maxDistance: number().int().min(0).optional(),
26086
26333
  minClusterSize: number().int().min(2).optional(),
@@ -26094,7 +26341,13 @@ method(object({
26094
26341
  }), method(object({ plateId: string() }), _void(), {
26095
26342
  kind: "mutation",
26096
26343
  auth: "admin"
26097
- }), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
26344
+ }), method(object({
26345
+ /** Inline {@link VehicleSchema.coverBase64} on every row. Default
26346
+ * `false` — the vehicle twin of `faceGallery.listIdentities`'s
26347
+ * option; `coverMediaKey` + the `event-media` plane carry the picture.
26348
+ * INPUT field: stripped by the hub router until the train ships, which
26349
+ * resolves to `false` and is exactly the intended default. */
26350
+ includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
26098
26351
  kind: "mutation",
26099
26352
  auth: "admin"
26100
26353
  }), method(object({
@@ -32031,6 +32284,12 @@ Object.freeze({
32031
32284
  addonId: null,
32032
32285
  access: "view"
32033
32286
  },
32287
+ "deviceManager.getBindingsBatch": {
32288
+ capName: "device-manager",
32289
+ capScope: "system",
32290
+ addonId: null,
32291
+ access: "view"
32292
+ },
32034
32293
  "deviceManager.getChildren": {
32035
32294
  capName: "device-manager",
32036
32295
  capScope: "system",
@@ -32091,6 +32350,12 @@ Object.freeze({
32091
32350
  addonId: null,
32092
32351
  access: "view"
32093
32352
  },
32353
+ "deviceManager.getLinkedDevicesBatch": {
32354
+ capName: "device-manager",
32355
+ capScope: "system",
32356
+ addonId: null,
32357
+ access: "view"
32358
+ },
32094
32359
  "deviceManager.getRoleDisplayDefaults": {
32095
32360
  capName: "device-manager",
32096
32361
  capScope: "system",
@@ -33795,6 +34060,12 @@ Object.freeze({
33795
34060
  addonId: null,
33796
34061
  access: "create"
33797
34062
  },
34063
+ "pipelineAnalytics.cancelRelocateMedia": {
34064
+ capName: "pipeline-analytics",
34065
+ capScope: "device",
34066
+ addonId: null,
34067
+ access: "create"
34068
+ },
33798
34069
  "pipelineAnalytics.cancelStorageMigrationMove": {
33799
34070
  capName: "pipeline-analytics",
33800
34071
  capScope: "device",
@@ -33861,6 +34132,12 @@ Object.freeze({
33861
34132
  addonId: null,
33862
34133
  access: "view"
33863
34134
  },
34135
+ "pipelineAnalytics.getGroup": {
34136
+ capName: "pipeline-analytics",
34137
+ capScope: "device",
34138
+ addonId: null,
34139
+ access: "view"
34140
+ },
33864
34141
  "pipelineAnalytics.getKeyEvents": {
33865
34142
  capName: "pipeline-analytics",
33866
34143
  capScope: "device",
@@ -33945,6 +34222,12 @@ Object.freeze({
33945
34222
  addonId: null,
33946
34223
  access: "view"
33947
34224
  },
34225
+ "pipelineAnalytics.listGroups": {
34226
+ capName: "pipeline-analytics",
34227
+ capScope: "device",
34228
+ addonId: null,
34229
+ access: "view"
34230
+ },
33948
34231
  "pipelineAnalytics.listOpsLog": {
33949
34232
  capName: "pipeline-analytics",
33950
34233
  capScope: "device",
@@ -33957,6 +34240,12 @@ Object.freeze({
33957
34240
  addonId: null,
33958
34241
  access: "view"
33959
34242
  },
34243
+ "pipelineAnalytics.listRelocateMediaJobs": {
34244
+ capName: "pipeline-analytics",
34245
+ capScope: "device",
34246
+ addonId: null,
34247
+ access: "view"
34248
+ },
33960
34249
  "pipelineAnalytics.listRetrainAnnotations": {
33961
34250
  capName: "pipeline-analytics",
33962
34251
  capScope: "device",
@@ -34035,6 +34324,12 @@ Object.freeze({
34035
34324
  addonId: null,
34036
34325
  access: "create"
34037
34326
  },
34327
+ "pipelineAnalytics.relocateMedia": {
34328
+ capName: "pipeline-analytics",
34329
+ capScope: "device",
34330
+ addonId: null,
34331
+ access: "create"
34332
+ },
34038
34333
  "pipelineAnalytics.restageRetrainTrack": {
34039
34334
  capName: "pipeline-analytics",
34040
34335
  capScope: "device",
@@ -36726,6 +37021,11 @@ Object.freeze({
36726
37021
  form: "single",
36727
37022
  optional: false
36728
37023
  }],
37024
+ "deviceManager.getBindingsBatch": [{
37025
+ name: "deviceIds",
37026
+ form: "array",
37027
+ optional: false
37028
+ }],
36729
37029
  "deviceManager.getChildren": [{
36730
37030
  name: "parentDeviceId",
36731
37031
  form: "single",
@@ -36771,6 +37071,11 @@ Object.freeze({
36771
37071
  form: "single",
36772
37072
  optional: false
36773
37073
  }],
37074
+ "deviceManager.getLinkedDevicesBatch": [{
37075
+ name: "deviceIds",
37076
+ form: "array",
37077
+ optional: false
37078
+ }],
36774
37079
  "deviceManager.getSettingsSchema": [{
36775
37080
  name: "deviceId",
36776
37081
  form: "single",
@@ -36791,6 +37096,11 @@ Object.freeze({
36791
37096
  form: "single",
36792
37097
  optional: false
36793
37098
  }],
37099
+ "deviceManager.listAll": [{
37100
+ name: "deviceIds",
37101
+ form: "array",
37102
+ optional: true
37103
+ }],
36794
37104
  "deviceManager.loadConfig": [{
36795
37105
  name: "deviceId",
36796
37106
  form: "single",
@@ -37364,6 +37674,11 @@ Object.freeze({
37364
37674
  form: "single",
37365
37675
  optional: false
37366
37676
  }],
37677
+ "pipelineAnalytics.getGroup": [{
37678
+ name: "deviceId",
37679
+ form: "single",
37680
+ optional: false
37681
+ }],
37367
37682
  "pipelineAnalytics.getKeyEvents": [{
37368
37683
  name: "deviceId",
37369
37684
  form: "single",
@@ -37419,6 +37734,11 @@ Object.freeze({
37419
37734
  form: "array",
37420
37735
  optional: false
37421
37736
  }],
37737
+ "pipelineAnalytics.listGroups": [{
37738
+ name: "deviceIds",
37739
+ form: "array",
37740
+ optional: false
37741
+ }],
37422
37742
  "pipelineAnalytics.listOpsLog": [{
37423
37743
  name: "deviceId",
37424
37744
  form: "single",