@camstack/addon-provider-tuya 0.2.27 → 0.2.29

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
@@ -6621,6 +6621,13 @@ var BaseAddon = class {
6621
6621
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
6622
6622
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
6623
6623
  _registeredCapNames = [];
6624
+ /**
6625
+ * True only after `readAddonStore` actually answered. Constructor
6626
+ * defaults look like stored config when the store is down — a forked
6627
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
6628
+ * mode, 2026-08-25) is not "the operator chose this".
6629
+ */
6630
+ settingsStoreReady = false;
6624
6631
  /** Default config values. Provided via constructor. */
6625
6632
  defaults;
6626
6633
  constructor(defaults) {
@@ -7021,7 +7028,9 @@ var BaseAddon = class {
7021
7028
  ];
7022
7029
  let lastErr;
7023
7030
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
7024
- return await settings.readAddonStore() ?? {};
7031
+ const stored = await settings.readAddonStore() ?? {};
7032
+ this.settingsStoreReady = true;
7033
+ return stored;
7025
7034
  } catch (err) {
7026
7035
  lastErr = err;
7027
7036
  const msg = err instanceof Error ? err.message : String(err);
@@ -7029,6 +7038,7 @@ var BaseAddon = class {
7029
7038
  if (attempt === delaysMs.length) break;
7030
7039
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
7031
7040
  }
7041
+ this.settingsStoreReady = false;
7032
7042
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
7033
7043
  return {};
7034
7044
  }
@@ -7440,7 +7450,7 @@ function method(input, output, options) {
7440
7450
  input,
7441
7451
  output,
7442
7452
  kind: options?.kind ?? "query",
7443
- auth: options?.auth ?? "protected",
7453
+ ...options?.auth !== void 0 ? { auth: options.auth } : {},
7444
7454
  ...options?.access !== void 0 ? { access: options.access } : {},
7445
7455
  ...options?.caller !== void 0 ? { caller: options.caller } : {},
7446
7456
  timeoutMs: options?.timeoutMs
@@ -7464,7 +7474,7 @@ function event(data) {
7464
7474
  }
7465
7475
  var StaticDirOutputSchema$1 = object({ staticDir: string() });
7466
7476
  var VersionOutputSchema$1 = object({ version: string() });
7467
- method(_void(), StaticDirOutputSchema$1), method(_void(), VersionOutputSchema$1);
7477
+ method(_void(), StaticDirOutputSchema$1, { auth: "admin" }), method(_void(), VersionOutputSchema$1, { auth: "admin" });
7468
7478
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
7469
7479
  DeviceType["Camera"] = "camera";
7470
7480
  DeviceType["Hub"] = "hub";
@@ -7785,7 +7795,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
7785
7795
  }({});
7786
7796
  var StaticDirOutputSchema = object({ staticDir: string() });
7787
7797
  var VersionOutputSchema = object({ version: string() });
7788
- method(_void(), StaticDirOutputSchema), method(_void(), VersionOutputSchema);
7798
+ method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
7789
7799
  /**
7790
7800
  * device-ops — device-scoped cap that unifies the per-IDevice operations
7791
7801
  * previously routed through the `.device-ops` Moleculer bridge service.
@@ -8411,24 +8421,6 @@ var RecordingRetentionSchema = object({
8411
8421
  maxSizeGb: number().min(0).optional()
8412
8422
  });
8413
8423
  /**
8414
- * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
8415
- * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
8416
- * previews at. Five graduated steps; absent on a config = `standard` (the
8417
- * shipped default, matching `sheet-geometry`/`sheet-composer`).
8418
- *
8419
- * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
8420
- * Each window's index sidecar carries its own tile dims, so a camera whose
8421
- * preset changed over time renders every historical window at the dims it was
8422
- * written with.
8423
- */
8424
- var ScrubThumbnailPresetSchema = _enum([
8425
- "minimal",
8426
- "low",
8427
- "standard",
8428
- "high",
8429
- "max"
8430
- ]);
8431
- /**
8432
8424
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
8433
8425
  *
8434
8426
  * `bands` is the ONLY authored recording intent: what to record, when, and on
@@ -8436,7 +8428,11 @@ var ScrubThumbnailPresetSchema = _enum([
8436
8428
  * other field is a storage knob (profiles, segment length, retention, scrub).
8437
8429
  *
8438
8430
  * STRICT on purpose: the legacy authoring surface (`schedule`/`schedules`/
8439
- * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30.
8431
+ * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30,
8432
+ * and `scrubThumbnails` — a five-step fidelity knob for a sprite tier that was
8433
+ * deleted on 2026-07-24 and had ZERO consumers in the recorder — on 2026-08-25
8434
+ * (D62: a switch that writes a store nobody reads is worse than no switch).
8435
+ * Stored rows keep loading: the READ schema is `.strip()` (config-store.ts).
8440
8436
  * A stale caller must fail loudly — silently stripping its legacy intent would
8441
8437
  * persist a band-less config, i.e. silently stop recording the camera.
8442
8438
  */
@@ -8459,14 +8455,7 @@ var RecordingConfigSchema = object({
8459
8455
  * "off" is the absence of a covering band, never a band value.
8460
8456
  */
8461
8457
  bands: array(RecordingBandSchema).default([]),
8462
- retention: RecordingRetentionSchema.optional(),
8463
- /**
8464
- * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
8465
- * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
8466
- * windows only — existing sheets are immutable, and each window's index
8467
- * carries its own tile dims so mixed-preset history renders correctly.
8468
- */
8469
- scrubThumbnails: ScrubThumbnailPresetSchema.optional()
8458
+ retention: RecordingRetentionSchema.optional()
8470
8459
  }).strict();
8471
8460
  /**
8472
8461
  * Entity-relocation job state (storage entity-routing spec, Phase 4).
@@ -8542,10 +8531,11 @@ var RelocateFootageInputSchema = object({
8542
8531
  * `RecordingConfig.enabled` or camera wrapper bindings. */
8543
8532
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
8544
8533
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
8545
- var StorageMigrationMediaMoveInputSchema = object({
8534
+ var RelocateMediaInputSchema = object({
8546
8535
  toLocationId: string(),
8547
8536
  throttleMbps: number().min(1).max(1e3).optional()
8548
- }).extend({ leaseId: string().min(1) });
8537
+ });
8538
+ var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
8549
8539
  /** The independently selectable logical storage classes. `recordings`
8550
8540
  * encompasses the high and mid segment profiles; `recordingsLow` is low
8551
8541
  * segments; `eventMedia` is post-analysis blobs. */
@@ -8840,7 +8830,26 @@ var LabelDefinitionSchema = object({
8840
8830
  description: string().optional(),
8841
8831
  icon: string().optional()
8842
8832
  });
8843
- var ClassMapDefinitionSchema = object({
8833
+ /**
8834
+ * Wire schema for a per-model CATALOG classMap override
8835
+ * (`ModelCatalogEntry.classMap` / `ModelConvertMetadata.classMap`) —
8836
+ * restricted to {@link CLASS_MAP_MACRO_TARGETS}, the only macros the
8837
+ * detection pipeline executor actually routes.
8838
+ *
8839
+ * This is deliberately a DIFFERENT, narrower shape than the general-purpose
8840
+ * `ClassMapDefinition` interface above (e.g. `IDetectionAddon.getClassMap()`
8841
+ * and the audio `YAMNET_TO_MACRO` catalog both use macro targets outside this
8842
+ * enum) — the two used to share the name `ClassMapDefinition`/
8843
+ * `ClassMapDefinitionSchema`, which made the schema-type-twin guard
8844
+ * (`scripts/check-schema-type-twins.ts`) flag them as a duplicated shape. They
8845
+ * are not: it is two different concepts colliding on a name. Keep this type
8846
+ * under its own name rather than reusing `ClassMapDefinition` — reusing it
8847
+ * would either narrow every `ClassMapDefinition` consumer to the four
8848
+ * detection macros (breaking `YAMNET_TO_MACRO`) or drop the validation this
8849
+ * schema exists for (see the "rejects a classMap whose target is not a
8850
+ * detection macro" test in `model-catalog-schema.test.ts`).
8851
+ */
8852
+ var DetectionCatalogClassMapSchema = object({
8844
8853
  mapping: record(string(), _enum([
8845
8854
  "person",
8846
8855
  "vehicle",
@@ -8932,6 +8941,12 @@ var ModelVariantGroupSchema = object({
8932
8941
  */
8933
8942
  resolution: number().int().positive().optional()
8934
8943
  });
8944
+ var ModelProviderIdSchema = _enum([
8945
+ "camstack",
8946
+ "frigate",
8947
+ "scrypted",
8948
+ "custom"
8949
+ ]);
8935
8950
  var ModelCatalogEntrySchema = object({
8936
8951
  id: string(),
8937
8952
  name: string(),
@@ -9029,11 +9044,17 @@ var ModelCatalogEntrySchema = object({
9029
9044
  */
9030
9045
  group: ModelVariantGroupSchema.optional(),
9031
9046
  /**
9047
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
9048
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
9049
+ * persisted before this field existed (`inferModelProvider` fills those).
9050
+ */
9051
+ provider: ModelProviderIdSchema.optional(),
9052
+ /**
9032
9053
  * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
9033
9054
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
9034
9055
  * labels already ARE the CamStack macros (Scrypted identity map).
9035
9056
  */
9036
- classMap: ClassMapDefinitionSchema.optional()
9057
+ classMap: DetectionCatalogClassMapSchema.optional()
9037
9058
  });
9038
9059
  var ConvertTargetSchema = discriminatedUnion("format", [object({
9039
9060
  format: literal("openvino"),
@@ -9063,7 +9084,7 @@ var ModelConvertMetadataSchema = object({
9063
9084
  "segmentation"
9064
9085
  ]),
9065
9086
  faceAlignment: boolean().optional(),
9066
- classMap: ClassMapDefinitionSchema.optional()
9087
+ classMap: DetectionCatalogClassMapSchema.optional()
9067
9088
  });
9068
9089
  var ConvertResultSchema = object({
9069
9090
  entry: ModelCatalogEntrySchema,
@@ -9926,7 +9947,7 @@ var AddonPageDeclarationSchema = object({
9926
9947
  /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
9927
9948
  sectionLabel: string().optional()
9928
9949
  });
9929
- method(_void(), array(AddonPageDeclarationSchema).readonly());
9950
+ method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
9930
9951
  var AddonHttpRouteSchema = object({
9931
9952
  method: _enum([
9932
9953
  "GET",
@@ -10161,7 +10182,7 @@ var WidgetMetadataSchema = object({
10161
10182
  defaultColumns: number().int().min(1).max(12).default(6),
10162
10183
  defaultRows: number().int().min(1).max(12).default(1)
10163
10184
  });
10164
- method(_void(), array(WidgetMetadataSchema).readonly());
10185
+ method(_void(), array(WidgetMetadataSchema).readonly(), { auth: "admin" });
10165
10186
  /**
10166
10187
  * `addon-widgets` — system-scoped singleton aggregator cap. Public-facing
10167
10188
  * surface that admin-ui consumes through `useAddonWidgetsListWidgets()`.
@@ -11785,7 +11806,7 @@ var CustomModelDescriptorSchema = object({
11785
11806
  stepId: string(),
11786
11807
  entry: ModelCatalogEntrySchema
11787
11808
  });
11788
- method(_void(), array(CustomModelDescriptorSchema).readonly());
11809
+ method(_void(), array(CustomModelDescriptorSchema).readonly(), { auth: "admin" });
11789
11810
  /**
11790
11811
  * Query filter for settings-store collections.
11791
11812
  */
@@ -11872,7 +11893,8 @@ method(object({
11872
11893
  }), _void(), { kind: "mutation" }), method(object({
11873
11894
  namespace: string().optional(),
11874
11895
  collection: string(),
11875
- filter: QueryFilterSchema.optional()
11896
+ filter: QueryFilterSchema.optional(),
11897
+ columns: array(string()).readonly().optional()
11876
11898
  }), array(SettingsRecordSchema).readonly()), method(object({
11877
11899
  namespace: string().optional(),
11878
11900
  collection: string(),
@@ -11935,46 +11957,87 @@ var EngineInfoSchema = object({
11935
11957
  kind: _enum(["relational", "vector"]),
11936
11958
  displayName: string()
11937
11959
  });
11938
- method(_void(), EngineInfoSchema), method(object({
11960
+ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
11939
11961
  namespace: string().optional(),
11940
11962
  collection: string(),
11941
11963
  key: string()
11942
- }), unknown()), method(object({
11964
+ }), unknown(), { auth: "admin" }), method(object({
11943
11965
  namespace: string().optional(),
11944
11966
  collection: string(),
11945
11967
  key: string(),
11946
11968
  value: unknown()
11947
- }), _void(), { kind: "mutation" }), method(object({
11969
+ }), _void(), {
11970
+ kind: "mutation",
11971
+ auth: "admin"
11972
+ }), method(object({
11948
11973
  namespace: string().optional(),
11949
11974
  collection: string(),
11950
- filter: QueryFilterSchema.optional()
11951
- }), array(SettingsRecordSchema).readonly()), method(object({
11975
+ filter: QueryFilterSchema.optional(),
11976
+ /**
11977
+ * SQL-level column projection — MUST mirror `settings-store.query`.
11978
+ *
11979
+ * ⚠ An earlier version of this comment blamed Zod stripping, and that
11980
+ * was wrong — corrected 2026-08-26 after the hop map
11981
+ * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
11982
+ * There is **no Zod parse at all** between the door and the engine: the
11983
+ * dispatcher forwards the payload verbatim and UDS carries it whole. A
11984
+ * field declared here reaches `SqliteSettingsBackend` either way.
11985
+ *
11986
+ * What actually lost `columns` was the THIRD declaration of this shape:
11987
+ * `SettingsQueryInput` in `interfaces/storage.ts`, a hand-written TS
11988
+ * interface the engine destructures from. The field existed on both
11989
+ * schemas and the engine still never read it, because nothing checks a
11990
+ * registered provider against `InferProvider<cap>` —
11991
+ * `ProviderRegistration.provider` is typed `object`.
11992
+ *
11993
+ * It is declared here anyway, and must stay in step with
11994
+ * `settings-store.query`: a caller reading only the cap definitions has
11995
+ * to be able to see that this call carries a projection.
11996
+ * `data-door-schema-parity.spec.ts` keeps the two aligned.
11997
+ */
11998
+ columns: array(string()).readonly().optional()
11999
+ }), array(SettingsRecordSchema).readonly(), { auth: "admin" }), method(object({
11952
12000
  namespace: string().optional(),
11953
12001
  collection: string(),
11954
12002
  record: SettingsRecordSchema
11955
- }), _void(), { kind: "mutation" }), method(object({
12003
+ }), _void(), {
12004
+ kind: "mutation",
12005
+ auth: "admin"
12006
+ }), method(object({
11956
12007
  namespace: string().optional(),
11957
12008
  collection: string(),
11958
12009
  id: string(),
11959
12010
  data: record(string(), unknown())
11960
- }), _void(), { kind: "mutation" }), method(object({
12011
+ }), _void(), {
12012
+ kind: "mutation",
12013
+ auth: "admin"
12014
+ }), method(object({
11961
12015
  namespace: string().optional(),
11962
12016
  collection: string(),
11963
12017
  key: string()
11964
- }), _void(), { kind: "mutation" }), method(object({
12018
+ }), _void(), {
12019
+ kind: "mutation",
12020
+ auth: "admin"
12021
+ }), method(object({
11965
12022
  namespace: string().optional(),
11966
12023
  collection: string(),
11967
12024
  filter: MutationFilterSchema
11968
- }), object({ deleted: number().int() }), { kind: "mutation" }), method(object({
12025
+ }), object({ deleted: number().int() }), {
12026
+ kind: "mutation",
12027
+ auth: "admin"
12028
+ }), method(object({
11969
12029
  namespace: string().optional(),
11970
12030
  collection: string(),
11971
12031
  filter: MutationFilterSchema,
11972
12032
  data: record(string(), unknown())
11973
- }), object({ updated: number().int() }), { kind: "mutation" }), method(object({
12033
+ }), object({ updated: number().int() }), {
12034
+ kind: "mutation",
12035
+ auth: "admin"
12036
+ }), method(object({
11974
12037
  namespace: string().optional(),
11975
12038
  collection: string(),
11976
12039
  filter: QueryFilterSchema.optional()
11977
- }), number()), method(object({
12040
+ }), number(), { auth: "admin" }), method(object({
11978
12041
  namespace: string().optional(),
11979
12042
  collection: string(),
11980
12043
  field: string(),
@@ -11984,15 +12047,18 @@ method(_void(), EngineInfoSchema), method(object({
11984
12047
  }), array(object({
11985
12048
  bucket: number().int(),
11986
12049
  count: number().int()
11987
- })).readonly()), method(object({
12050
+ })).readonly(), { auth: "admin" }), method(object({
11988
12051
  namespace: string().optional(),
11989
12052
  collection: string()
11990
- }), boolean()), method(object({
12053
+ }), boolean(), { auth: "admin" }), method(object({
11991
12054
  namespace: string().optional(),
11992
12055
  collection: string(),
11993
12056
  columns: array(CollectionColumnSchema).readonly(),
11994
12057
  indexes: array(CollectionIndexSchema).readonly().optional()
11995
- }), _void(), { kind: "mutation" });
12058
+ }), _void(), {
12059
+ kind: "mutation",
12060
+ auth: "admin"
12061
+ });
11996
12062
  /**
11997
12063
  * shm ring usage stats for a `frameSink: 'shm'` decoder session —
11998
12064
  * exposed via `decoder.getShmStats` so downstream consumers can
@@ -12837,6 +12903,27 @@ var LinkedDeviceSchema = object({
12837
12903
  features: array(string()),
12838
12904
  producesTrackedEvents: boolean().optional()
12839
12905
  });
12906
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
12907
+ * The batch answer needs the tag; the single-device answer already has it
12908
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
12909
+ var LinkedDevicesForDeviceSchema = object({
12910
+ deviceId: number(),
12911
+ mode: LinkedDevicesModeSchema,
12912
+ devices: array(LinkedDeviceSchema)
12913
+ });
12914
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
12915
+ * `getAllBindings` all answer in. Declared once: three copies of the same
12916
+ * object literal is exactly how the three drift apart. */
12917
+ var DeviceBindingsForDeviceSchema = object({
12918
+ deviceId: number(),
12919
+ entries: array(object({
12920
+ capName: string(),
12921
+ kind: _enum(["native", "wrapped"]),
12922
+ providerAddonId: string(),
12923
+ providerNodeId: string(),
12924
+ nativeAddonId: string()
12925
+ }))
12926
+ });
12840
12927
  var SavedDeviceRowSchema = object({
12841
12928
  /** Numeric id reserved at allocateDeviceId time. */
12842
12929
  id: number(),
@@ -13062,11 +13149,25 @@ method(object({
13062
13149
  projection: _enum(["full", "slim"]).optional(),
13063
13150
  /** Return only camera devices. Filtering server-side instead of
13064
13151
  * shipping 293 rows to find 12. */
13065
- isCamera: boolean().optional()
13152
+ isCamera: boolean().optional(),
13153
+ /**
13154
+ * Return only these device ids. For the caller that already KNOWS the
13155
+ * handful it wants and needs a field the id-bearing answer does not
13156
+ * carry — the viewer's linked-devices panel joins `type` and `online`
13157
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
13158
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
13159
+ * refetches on the reconcile interval, on a phone.
13160
+ *
13161
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
13162
+ * keys rather than rejecting them (verified against the live hub
13163
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
13164
+ * it answers today and the caller filters as it already does.
13165
+ */
13166
+ deviceIds: array(number()).optional()
13066
13167
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
13067
13168
  mode: LinkedDevicesModeSchema,
13068
13169
  devices: array(LinkedDeviceSchema)
13069
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
13170
+ })), 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({
13070
13171
  deviceId: number(),
13071
13172
  values: record(string(), unknown())
13072
13173
  }), object({ success: literal(true) }), {
@@ -13093,25 +13194,7 @@ method(object({
13093
13194
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
13094
13195
  kind: "mutation",
13095
13196
  auth: "admin"
13096
- }), method(object({ deviceId: number() }), object({
13097
- deviceId: number(),
13098
- entries: array(object({
13099
- capName: string(),
13100
- kind: _enum(["native", "wrapped"]),
13101
- providerAddonId: string(),
13102
- providerNodeId: string(),
13103
- nativeAddonId: string()
13104
- }))
13105
- })), method(object({}), array(object({
13106
- deviceId: number(),
13107
- entries: array(object({
13108
- capName: string(),
13109
- kind: _enum(["native", "wrapped"]),
13110
- providerAddonId: string(),
13111
- providerNodeId: string(),
13112
- nativeAddonId: string()
13113
- }))
13114
- }))), method(object({
13197
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
13115
13198
  deviceId: number(),
13116
13199
  capName: string(),
13117
13200
  wrapperAddonId: string(),
@@ -13282,7 +13365,7 @@ method(object({
13282
13365
  crop: _instanceof(Uint8Array),
13283
13366
  width: number(),
13284
13367
  height: number()
13285
- }), EmbeddingResultSchema), method(object({ text: string() }), EmbeddingResultSchema), method(_void(), EmbeddingInfoSchema);
13368
+ }), EmbeddingResultSchema, { auth: "admin" }), method(object({ text: string() }), EmbeddingResultSchema, { auth: "admin" }), method(_void(), EmbeddingInfoSchema, { auth: "admin" });
13286
13369
  /**
13287
13370
  * filesystem-browse — per-node capability for browsing the node's local
13288
13371
  * filesystem. Reads are unconfined (whole filesystem, from `/` down); WRITES
@@ -13575,19 +13658,22 @@ method(LlmGenerateBaseInputSchema.extend({
13575
13658
  runtime: ManagedRuntimeConfigSchema,
13576
13659
  /** The managed profile's timeout, threaded by the hub provider. */
13577
13660
  timeoutMs: number().int().positive().optional()
13578
- }), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
13661
+ }), LlmGenerateResultSchema, {
13662
+ kind: "mutation",
13663
+ auth: "admin"
13664
+ }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
13579
13665
  kind: "mutation",
13580
13666
  auth: "admin"
13581
13667
  }), method(object({}), _void(), {
13582
13668
  kind: "mutation",
13583
13669
  auth: "admin"
13584
- }), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
13670
+ }), method(object({}), LlmRuntimeStatusSchema, { auth: "admin" }), method(object({ model: ManagedModelRefSchema }), _void(), {
13585
13671
  kind: "mutation",
13586
13672
  auth: "admin"
13587
13673
  }), method(object({ file: string() }), _void(), {
13588
13674
  kind: "mutation",
13589
13675
  auth: "admin"
13590
- }), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
13676
+ }), method(object({}), array(LlmNodeModelSchema), { auth: "admin" }), method(object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" });
13591
13677
  /**
13592
13678
  * `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
13593
13679
  * methods concat-fan across providers; single-row methods route to ONE
@@ -15521,12 +15607,15 @@ var NcOccupancyConditionSchema = object({
15521
15607
  * there is no second switch that can disagree with the first and every rule
15522
15608
  * authored before the decision migrates for free (`audioModeOf`):
15523
15609
  *
15524
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
15525
- * classifier labels with one of them. No window, no percentage:
15526
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
15527
- * `throttle` cooldown is the only brake. The per-label confidence floor is
15528
- * the analyzer's (`classificationMinScore`, per device) — a label only
15529
- * reaches this condition if the classifier was already confident enough.
15610
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
15611
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
15612
+ * `hitPercent` and `samplingSeconds` are still ignored — a percentage of
15613
+ * frames is the wrong question for a classifier that labels 1–3 frames
15614
+ * per episode. The count window is the brake that drops a single-frame
15615
+ * false positive; the rule's own `throttle` cooldown is the other. The
15616
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
15617
+ * per device) — a label only reaches this condition if the classifier was
15618
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
15530
15619
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
15531
15620
  * the condition: at least `hitPercent`% of the samples over
15532
15621
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -15553,14 +15642,22 @@ var NcOccupancyConditionSchema = object({
15553
15642
  * an operator who typed `dog` mean the same thing.
15554
15643
  */
15555
15644
  var NcAudioConditionSchema = object({
15556
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
15645
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
15557
15646
  labels: array(string().min(1)).min(1).optional(),
15558
15647
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
15559
15648
  dbThreshold: number().min(-96).max(0).optional(),
15560
15649
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
15561
15650
  hitPercent: number().int().min(1).max(100).default(60),
15562
15651
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
15563
- samplingSeconds: number().int().min(1).max(300).default(10)
15652
+ samplingSeconds: number().int().min(1).max(300).default(10),
15653
+ /**
15654
+ * LABEL MODE: how many labelled frames must land inside
15655
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
15656
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
15657
+ */
15658
+ confirmHits: number().int().min(1).max(20).optional(),
15659
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
15660
+ confirmWindowSec: number().int().min(1).max(60).optional()
15564
15661
  });
15565
15662
  /**
15566
15663
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -17087,7 +17184,7 @@ var OauthIntegrationDescriptorSchema = object({
17087
17184
  */
17088
17185
  refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
17089
17186
  });
17090
- method(_void(), OauthIntegrationDescriptorSchema);
17187
+ method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
17091
17188
  /**
17092
17189
  * pipeline-analytics — device-scoped wrapper cap. Refines raw
17093
17190
  * per-frame detections emitted by the pipeline runner into tracked
@@ -17934,6 +18031,46 @@ var RecentTracksPageSchema = object({
17934
18031
  /** Cursor for the next page, or null when this page is the last. */
17935
18032
  nextCursor: string().nullable()
17936
18033
  });
18034
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
18035
+ var LIST_GROUPS_MAX_LIMIT = 100;
18036
+ var AnalyticsGroupRecordSchema = object({
18037
+ id: string(),
18038
+ deviceId: number().int(),
18039
+ openedAt: number().int(),
18040
+ closedAt: number().int(),
18041
+ timestamp: number().int(),
18042
+ memberCount: number().int(),
18043
+ memberTrackIds: array(string()).readonly(),
18044
+ className: string(),
18045
+ classes: array(string()).readonly(),
18046
+ /** Relative event-media path, or null when the group has no picture yet. */
18047
+ mediaUrl: string().nullable(),
18048
+ singleton: boolean()
18049
+ });
18050
+ var AnalyticsGroupMemberSchema = object({
18051
+ trackId: string(),
18052
+ deviceId: number().int(),
18053
+ className: string(),
18054
+ firstSeen: number().int(),
18055
+ lastSeen: number().int(),
18056
+ mediaUrl: string().nullable()
18057
+ });
18058
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
18059
+ var ListGroupsQueryInput = object({
18060
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
18061
+ deviceIds: array(number()),
18062
+ /** Window lower bound on `closedAt` (inclusive). */
18063
+ since: number().optional(),
18064
+ /** Window upper bound on `openedAt` (inclusive). */
18065
+ until: number().optional(),
18066
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
18067
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
18068
+ cursor: string().optional()
18069
+ });
18070
+ var ListGroupsPageSchema = object({
18071
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
18072
+ nextCursor: string().nullable()
18073
+ });
17937
18074
  var KeyEventQueryInput = object({
17938
18075
  deviceId: number(),
17939
18076
  /** Window lower bound (track firstSeen ≥ since). */
@@ -18009,7 +18146,9 @@ var TrackCascadeCountsSchema = object({
18009
18146
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
18010
18147
  plates: number().int(),
18011
18148
  /** Per-track CLIP search vectors removed (best-effort). */
18012
- embeddings: number().int()
18149
+ embeddings: number().int(),
18150
+ /** Group membership + group rows removed with their last member (best-effort). */
18151
+ groups: number().int()
18013
18152
  });
18014
18153
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
18015
18154
  var DiskReconcileCountsSchema = object({
@@ -18155,7 +18294,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18155
18294
  * stationary registry). Default false: the timeline lists passages,
18156
18295
  * not parking records (operator decision, 2026-08-15). */
18157
18296
  includeStationary: boolean().optional()
18158
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
18297
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
18298
+ deviceId: number(),
18299
+ groupId: string().min(1)
18300
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
18159
18301
  kind: "mutation",
18160
18302
  auth: "admin"
18161
18303
  }), 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({
@@ -18237,6 +18379,15 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18237
18379
  }), method(object({ jobId: string() }), RelocateJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
18238
18380
  kind: "mutation",
18239
18381
  auth: "admin"
18382
+ }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
18383
+ kind: "mutation",
18384
+ auth: "admin"
18385
+ }), method(object({}), array(RelocateJobSchema).readonly(), {
18386
+ kind: "query",
18387
+ auth: "admin"
18388
+ }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
18389
+ kind: "mutation",
18390
+ auth: "admin"
18240
18391
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
18241
18392
  kind: "query",
18242
18393
  auth: "admin"
@@ -18391,6 +18542,11 @@ object({
18391
18542
  "jpeg"
18392
18543
  ])
18393
18544
  });
18545
+ /**
18546
+ * Process-local frame identity. It is serializable so it can ride an in-process
18547
+ * capability call, but `registryId` deliberately prevents resolution in any
18548
+ * other process or execution group.
18549
+ */
18394
18550
  var FrameRefSchema = object({
18395
18551
  registryId: string().min(1),
18396
18552
  id: string().min(1),
@@ -18465,7 +18621,8 @@ var PipelineModelOptionSchema = object({
18465
18621
  sizeMB: number()
18466
18622
  })),
18467
18623
  group: ModelVariantGroupSchema.optional(),
18468
- legacy: boolean().optional()
18624
+ legacy: boolean().optional(),
18625
+ provider: ModelProviderIdSchema.optional()
18469
18626
  });
18470
18627
  var ConfigFieldBridge = custom();
18471
18628
  var PipelineAddonSchemaSchema = object({
@@ -20595,7 +20752,7 @@ method(object({
20595
20752
  * linking rather than produce an eternal token.
20596
20753
  */
20597
20754
  ttlSec: union([number().int().positive(), literal("never")]).optional()
20598
- }), object({ token: string() })), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable());
20755
+ }), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
20599
20756
  var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
20600
20757
  providerId: string().min(1),
20601
20758
  displayName: string().min(1),
@@ -20690,10 +20847,13 @@ var EvictResultSchema = object({
20690
20847
  /** True when the provider has nothing left it is willing to drop on this location. */
20691
20848
  exhausted: boolean()
20692
20849
  });
20693
- method(object({ locationId: string() }), EvictableUsageSchema), method(object({
20850
+ method(object({ locationId: string() }), EvictableUsageSchema, { auth: "admin" }), method(object({
20694
20851
  locationId: string(),
20695
20852
  targetBytes: number().int().positive()
20696
- }), EvictResultSchema, { kind: "mutation" });
20853
+ }), EvictResultSchema, {
20854
+ kind: "mutation",
20855
+ auth: "admin"
20856
+ });
20697
20857
  method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin" }), method(StorageMigrationInputSchema, object({ jobId: string() }), {
20698
20858
  kind: "mutation",
20699
20859
  auth: "admin"
@@ -20753,26 +20913,50 @@ var ReadChunkInputSchema = object({
20753
20913
  length: number()
20754
20914
  });
20755
20915
  var EndDownloadInputSchema = object({ downloadId: string() });
20756
- method(_void(), ProviderInfoSchema), method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }), method(object({
20916
+ method(_void(), ProviderInfoSchema, { auth: "admin" }), method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }), method(object({
20757
20917
  location: StorageLocationSchema,
20758
20918
  relativePath: string()
20759
- }), string()), method(object({
20919
+ }), string(), { auth: "admin" }), method(object({
20760
20920
  location: StorageLocationSchema,
20761
20921
  relativePath: string(),
20762
20922
  data: _instanceof(Uint8Array)
20763
- }), _void(), { kind: "mutation" }), method(object({
20923
+ }), _void(), {
20924
+ kind: "mutation",
20925
+ auth: "admin"
20926
+ }), method(object({
20764
20927
  location: StorageLocationSchema,
20765
20928
  relativePath: string()
20766
- }), _instanceof(Uint8Array)), method(object({
20929
+ }), _instanceof(Uint8Array), { auth: "admin" }), method(object({
20767
20930
  location: StorageLocationSchema,
20768
20931
  relativePath: string()
20769
- }), boolean()), method(object({
20932
+ }), boolean(), { auth: "admin" }), method(object({
20770
20933
  location: StorageLocationSchema,
20771
20934
  prefix: string().optional()
20772
- }), array(string()).readonly()), method(object({
20935
+ }), array(string()).readonly(), { auth: "admin" }), method(object({
20773
20936
  location: StorageLocationSchema,
20774
20937
  relativePath: string()
20775
- }), _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" });
20938
+ }), _void(), {
20939
+ kind: "mutation",
20940
+ auth: "admin"
20941
+ }), method(object({ location: StorageLocationSchema }), number().nullable(), { auth: "admin" }), method(BeginUploadInputSchema, BeginUploadResultSchema, {
20942
+ kind: "mutation",
20943
+ auth: "admin"
20944
+ }), method(WriteChunkInputSchema, _void(), {
20945
+ kind: "mutation",
20946
+ auth: "admin"
20947
+ }), method(FinalizeUploadInputSchema, _void(), {
20948
+ kind: "mutation",
20949
+ auth: "admin"
20950
+ }), method(AbortUploadInputSchema, _void(), {
20951
+ kind: "mutation",
20952
+ auth: "admin"
20953
+ }), method(BeginDownloadInputSchema, BeginDownloadResultSchema, {
20954
+ kind: "mutation",
20955
+ auth: "admin"
20956
+ }), method(ReadChunkInputSchema, _instanceof(Uint8Array), { auth: "admin" }), method(EndDownloadInputSchema, _void(), {
20957
+ kind: "mutation",
20958
+ auth: "admin"
20959
+ });
20776
20960
  /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
20777
20961
  var ProfileSettingsSchemaBridge = unknown().nullable();
20778
20962
  var ProfileSettingsBagSchema = record(string(), unknown());
@@ -21030,7 +21214,8 @@ method(object({
21030
21214
  access: "create"
21031
21215
  }), method(object({ userId: string().optional() }), object({ optionsJSON: record(string(), unknown()) }), {
21032
21216
  kind: "mutation",
21033
- access: "view"
21217
+ access: "view",
21218
+ auth: "admin"
21034
21219
  }), method(object({
21035
21220
  /** Required — the user the assertion belongs to (verified). */
21036
21221
  userId: string(),
@@ -21038,10 +21223,12 @@ method(object({
21038
21223
  response: record(string(), unknown())
21039
21224
  }), object({ verified: boolean() }), {
21040
21225
  kind: "mutation",
21041
- access: "view"
21226
+ access: "view",
21227
+ auth: "admin"
21042
21228
  }), method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
21043
21229
  kind: "mutation",
21044
- access: "view"
21230
+ access: "view",
21231
+ auth: "admin"
21045
21232
  }), method(object({
21046
21233
  /** AuthenticationResponseJSON from the browser. */
21047
21234
  response: record(string(), unknown()) }), object({
@@ -21049,7 +21236,8 @@ response: record(string(), unknown()) }), object({
21049
21236
  userId: string().nullable()
21050
21237
  }), {
21051
21238
  kind: "mutation",
21052
- access: "view"
21239
+ access: "view",
21240
+ auth: "admin"
21053
21241
  }), method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }), method(object({
21054
21242
  userId: string(),
21055
21243
  credentialId: string()
@@ -21221,7 +21409,19 @@ var VectorStatsResultSchema = object({
21221
21409
  /** False when the backend ranks approximately. */
21222
21410
  exact: boolean()
21223
21411
  });
21224
- 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);
21412
+ method(VectorDeclareIndexInputSchema, _void(), {
21413
+ kind: "mutation",
21414
+ auth: "admin"
21415
+ }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
21416
+ kind: "mutation",
21417
+ auth: "admin"
21418
+ }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
21419
+ kind: "mutation",
21420
+ auth: "admin"
21421
+ }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
21422
+ kind: "mutation",
21423
+ auth: "admin"
21424
+ }), method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" });
21225
21425
  var ClipSchema = object({
21226
21426
  /** Opaque, provider-namespaced id. The default provider encodes the time
21227
21427
  * window so `getClipPlayback` is self-contained (no event re-query). */
@@ -23682,7 +23882,27 @@ var MediaFileLiteSchema$1 = object({
23682
23882
  sizeBytes: number(),
23683
23883
  timestamp: number()
23684
23884
  });
23685
- method(_void(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
23885
+ method(object({
23886
+ /**
23887
+ * Inline {@link IdentitySchema.coverBase64} on every row.
23888
+ *
23889
+ * Default `false`, the same inversion `listRecentFaces` and
23890
+ * `listPlates` took on 2026-08-25 (see `include-crops-default.ts` for
23891
+ * why the burden belongs on the caller that WANTS the bytes). Measured
23892
+ * on the live hub the same day: four identities cost 40,979 B with the
23893
+ * covers inline, ~10 KB of base64 per row, on a query this UI mounts
23894
+ * four times and the viewer holds at `staleTime: 30_000`.
23895
+ *
23896
+ * Nothing loses its avatar: `coverMediaKey` is already on every row and
23897
+ * the `event-media` plane serves that key `immutable` with an ETag.
23898
+ *
23899
+ * **This is an INPUT field, so it does not reach the addon until the
23900
+ * next train** — the hub router validates cap inputs against its own
23901
+ * compiled Zod and strips a key it does not know. Until then the
23902
+ * provider sees `undefined`, which resolves to `false`: the cheap shape
23903
+ * is what ships, and the opt-in becomes reachable when the train lands.
23904
+ */
23905
+ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
23686
23906
  kind: "mutation",
23687
23907
  auth: "admin"
23688
23908
  }), method(object({
@@ -26570,7 +26790,14 @@ var PlateInfoSchema = object({
26570
26790
  plateBbox: BoundingBoxSchema.optional(),
26571
26791
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
26572
26792
  keyFrameMediaKey: string().optional(),
26573
- base64: string().optional()
26793
+ base64: string().optional(),
26794
+ /**
26795
+ * Same crop as a data-plane URL, always present when the plate has a stored
26796
+ * crop. `getPlateByTrack` returns this and never inlines JPEG; `listPlates`
26797
+ * and `searchPlates` inline the crop only when their `includeCrops` input is
26798
+ * left at its `true` default.
26799
+ */
26800
+ cropUrl: string().optional()
26574
26801
  });
26575
26802
  var MediaFileLiteSchema = object({
26576
26803
  key: string(),
@@ -26588,14 +26815,34 @@ var PlateClusterSchema = object({
26588
26815
  });
26589
26816
  method(object({
26590
26817
  deviceId: number().int().optional(),
26591
- limit: number().int().positive().optional()
26818
+ limit: number().int().positive().optional(),
26819
+ /**
26820
+ * Inline the base64 crop on every row. Default `true` — the existing
26821
+ * behaviour, kept so no caller breaks.
26822
+ *
26823
+ * Set `false` once the caller renders {@link PlateInfo.cropUrl}.
26824
+ * Measured on the live hub at the 500 rows the Plates view asks for:
26825
+ * 879,403 B and 27.7 s with the crops inline, against a few KiB of
26826
+ * metadata without them — and the browser then caches the images.
26827
+ *
26828
+ * This is the plate twin of `faceGallery.listRecentFaces`'s option;
26829
+ * plates were the one gallery list left without it.
26830
+ *
26831
+ * **This is an INPUT field, so it does not reach the addon until the
26832
+ * next train.** The hub router validates cap inputs against its own
26833
+ * compiled Zod and strips a key it does not know. Until the train
26834
+ * ships, sending `false` is harmless and keeps the crops inline.
26835
+ */
26836
+ includeCrops: boolean().optional()
26592
26837
  }).optional(), array(PlateInfoSchema).readonly()), method(object({
26593
26838
  deviceId: number().int(),
26594
26839
  trackId: string()
26595
26840
  }), PlateInfoSchema.nullable()), method(object({ plateId: string() }), array(MediaFileLiteSchema).readonly()), method(object({
26596
26841
  text: string().min(1),
26597
26842
  maxDistance: number().int().min(0).optional(),
26598
- limit: number().int().positive().optional()
26843
+ limit: number().int().positive().optional(),
26844
+ /** See `listPlates.includeCrops`. Default `true`. */
26845
+ includeCrops: boolean().optional()
26599
26846
  }), array(PlateInfoSchema).readonly()), method(object({
26600
26847
  maxDistance: number().int().min(0).optional(),
26601
26848
  minClusterSize: number().int().min(2).optional(),
@@ -26609,7 +26856,13 @@ method(object({
26609
26856
  }), method(object({ plateId: string() }), _void(), {
26610
26857
  kind: "mutation",
26611
26858
  auth: "admin"
26612
- }), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
26859
+ }), method(object({
26860
+ /** Inline {@link VehicleSchema.coverBase64} on every row. Default
26861
+ * `false` — the vehicle twin of `faceGallery.listIdentities`'s
26862
+ * option; `coverMediaKey` + the `event-media` plane carry the picture.
26863
+ * INPUT field: stripped by the hub router until the train ships, which
26864
+ * resolves to `false` and is exactly the intended default. */
26865
+ includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
26613
26866
  kind: "mutation",
26614
26867
  auth: "admin"
26615
26868
  }), method(object({
@@ -32094,6 +32347,12 @@ Object.freeze({
32094
32347
  addonId: null,
32095
32348
  access: "view"
32096
32349
  },
32350
+ "deviceManager.getBindingsBatch": {
32351
+ capName: "device-manager",
32352
+ capScope: "system",
32353
+ addonId: null,
32354
+ access: "view"
32355
+ },
32097
32356
  "deviceManager.getChildren": {
32098
32357
  capName: "device-manager",
32099
32358
  capScope: "system",
@@ -32154,6 +32413,12 @@ Object.freeze({
32154
32413
  addonId: null,
32155
32414
  access: "view"
32156
32415
  },
32416
+ "deviceManager.getLinkedDevicesBatch": {
32417
+ capName: "device-manager",
32418
+ capScope: "system",
32419
+ addonId: null,
32420
+ access: "view"
32421
+ },
32157
32422
  "deviceManager.getRoleDisplayDefaults": {
32158
32423
  capName: "device-manager",
32159
32424
  capScope: "system",
@@ -33858,6 +34123,12 @@ Object.freeze({
33858
34123
  addonId: null,
33859
34124
  access: "create"
33860
34125
  },
34126
+ "pipelineAnalytics.cancelRelocateMedia": {
34127
+ capName: "pipeline-analytics",
34128
+ capScope: "device",
34129
+ addonId: null,
34130
+ access: "create"
34131
+ },
33861
34132
  "pipelineAnalytics.cancelStorageMigrationMove": {
33862
34133
  capName: "pipeline-analytics",
33863
34134
  capScope: "device",
@@ -33924,6 +34195,12 @@ Object.freeze({
33924
34195
  addonId: null,
33925
34196
  access: "view"
33926
34197
  },
34198
+ "pipelineAnalytics.getGroup": {
34199
+ capName: "pipeline-analytics",
34200
+ capScope: "device",
34201
+ addonId: null,
34202
+ access: "view"
34203
+ },
33927
34204
  "pipelineAnalytics.getKeyEvents": {
33928
34205
  capName: "pipeline-analytics",
33929
34206
  capScope: "device",
@@ -34008,6 +34285,12 @@ Object.freeze({
34008
34285
  addonId: null,
34009
34286
  access: "view"
34010
34287
  },
34288
+ "pipelineAnalytics.listGroups": {
34289
+ capName: "pipeline-analytics",
34290
+ capScope: "device",
34291
+ addonId: null,
34292
+ access: "view"
34293
+ },
34011
34294
  "pipelineAnalytics.listOpsLog": {
34012
34295
  capName: "pipeline-analytics",
34013
34296
  capScope: "device",
@@ -34020,6 +34303,12 @@ Object.freeze({
34020
34303
  addonId: null,
34021
34304
  access: "view"
34022
34305
  },
34306
+ "pipelineAnalytics.listRelocateMediaJobs": {
34307
+ capName: "pipeline-analytics",
34308
+ capScope: "device",
34309
+ addonId: null,
34310
+ access: "view"
34311
+ },
34023
34312
  "pipelineAnalytics.listRetrainAnnotations": {
34024
34313
  capName: "pipeline-analytics",
34025
34314
  capScope: "device",
@@ -34098,6 +34387,12 @@ Object.freeze({
34098
34387
  addonId: null,
34099
34388
  access: "create"
34100
34389
  },
34390
+ "pipelineAnalytics.relocateMedia": {
34391
+ capName: "pipeline-analytics",
34392
+ capScope: "device",
34393
+ addonId: null,
34394
+ access: "create"
34395
+ },
34101
34396
  "pipelineAnalytics.restageRetrainTrack": {
34102
34397
  capName: "pipeline-analytics",
34103
34398
  capScope: "device",
@@ -36789,6 +37084,11 @@ Object.freeze({
36789
37084
  form: "single",
36790
37085
  optional: false
36791
37086
  }],
37087
+ "deviceManager.getBindingsBatch": [{
37088
+ name: "deviceIds",
37089
+ form: "array",
37090
+ optional: false
37091
+ }],
36792
37092
  "deviceManager.getChildren": [{
36793
37093
  name: "parentDeviceId",
36794
37094
  form: "single",
@@ -36834,6 +37134,11 @@ Object.freeze({
36834
37134
  form: "single",
36835
37135
  optional: false
36836
37136
  }],
37137
+ "deviceManager.getLinkedDevicesBatch": [{
37138
+ name: "deviceIds",
37139
+ form: "array",
37140
+ optional: false
37141
+ }],
36837
37142
  "deviceManager.getSettingsSchema": [{
36838
37143
  name: "deviceId",
36839
37144
  form: "single",
@@ -36854,6 +37159,11 @@ Object.freeze({
36854
37159
  form: "single",
36855
37160
  optional: false
36856
37161
  }],
37162
+ "deviceManager.listAll": [{
37163
+ name: "deviceIds",
37164
+ form: "array",
37165
+ optional: true
37166
+ }],
36857
37167
  "deviceManager.loadConfig": [{
36858
37168
  name: "deviceId",
36859
37169
  form: "single",
@@ -37427,6 +37737,11 @@ Object.freeze({
37427
37737
  form: "single",
37428
37738
  optional: false
37429
37739
  }],
37740
+ "pipelineAnalytics.getGroup": [{
37741
+ name: "deviceId",
37742
+ form: "single",
37743
+ optional: false
37744
+ }],
37430
37745
  "pipelineAnalytics.getKeyEvents": [{
37431
37746
  name: "deviceId",
37432
37747
  form: "single",
@@ -37482,6 +37797,11 @@ Object.freeze({
37482
37797
  form: "array",
37483
37798
  optional: false
37484
37799
  }],
37800
+ "pipelineAnalytics.listGroups": [{
37801
+ name: "deviceIds",
37802
+ form: "array",
37803
+ optional: false
37804
+ }],
37485
37805
  "pipelineAnalytics.listOpsLog": [{
37486
37806
  name: "deviceId",
37487
37807
  form: "single",