@camstack/addon-auth 1.2.30 → 1.2.32

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.
@@ -5800,6 +5800,13 @@ var BaseAddon = class {
5800
5800
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5801
5801
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5802
5802
  _registeredCapNames = [];
5803
+ /**
5804
+ * True only after `readAddonStore` actually answered. Constructor
5805
+ * defaults look like stored config when the store is down — a forked
5806
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5807
+ * mode, 2026-08-25) is not "the operator chose this".
5808
+ */
5809
+ settingsStoreReady = false;
5803
5810
  /** Default config values. Provided via constructor. */
5804
5811
  defaults;
5805
5812
  constructor(defaults) {
@@ -6200,7 +6207,9 @@ var BaseAddon = class {
6200
6207
  ];
6201
6208
  let lastErr;
6202
6209
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6203
- return await settings.readAddonStore() ?? {};
6210
+ const stored = await settings.readAddonStore() ?? {};
6211
+ this.settingsStoreReady = true;
6212
+ return stored;
6204
6213
  } catch (err) {
6205
6214
  lastErr = err;
6206
6215
  const msg = err instanceof Error ? err.message : String(err);
@@ -6208,6 +6217,7 @@ var BaseAddon = class {
6208
6217
  if (attempt === delaysMs.length) break;
6209
6218
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6210
6219
  }
6220
+ this.settingsStoreReady = false;
6211
6221
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6212
6222
  return {};
6213
6223
  }
@@ -6619,7 +6629,7 @@ function method(input, output, options) {
6619
6629
  input,
6620
6630
  output,
6621
6631
  kind: options?.kind ?? "query",
6622
- auth: options?.auth ?? "protected",
6632
+ ...options?.auth !== void 0 ? { auth: options.auth } : {},
6623
6633
  ...options?.access !== void 0 ? { access: options.access } : {},
6624
6634
  ...options?.caller !== void 0 ? { caller: options.caller } : {},
6625
6635
  timeoutMs: options?.timeoutMs
@@ -6639,7 +6649,7 @@ function systemMethod(input, output, options) {
6639
6649
  }
6640
6650
  var StaticDirOutputSchema$1 = object({ staticDir: string() });
6641
6651
  var VersionOutputSchema$1 = object({ version: string() });
6642
- method(_void(), StaticDirOutputSchema$1), method(_void(), VersionOutputSchema$1);
6652
+ method(_void(), StaticDirOutputSchema$1, { auth: "admin" }), method(_void(), VersionOutputSchema$1, { auth: "admin" });
6643
6653
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
6644
6654
  DeviceType["Camera"] = "camera";
6645
6655
  DeviceType["Hub"] = "hub";
@@ -6960,7 +6970,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
6960
6970
  }({});
6961
6971
  var StaticDirOutputSchema = object({ staticDir: string() });
6962
6972
  var VersionOutputSchema = object({ version: string() });
6963
- method(_void(), StaticDirOutputSchema), method(_void(), VersionOutputSchema);
6973
+ method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
6964
6974
  /**
6965
6975
  * device-ops — device-scoped cap that unifies the per-IDevice operations
6966
6976
  * previously routed through the `.device-ops` Moleculer bridge service.
@@ -7705,24 +7715,6 @@ var RecordingRetentionSchema = object({
7705
7715
  maxSizeGb: number().min(0).optional()
7706
7716
  });
7707
7717
  /**
7708
- * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7709
- * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7710
- * previews at. Five graduated steps; absent on a config = `standard` (the
7711
- * shipped default, matching `sheet-geometry`/`sheet-composer`).
7712
- *
7713
- * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7714
- * Each window's index sidecar carries its own tile dims, so a camera whose
7715
- * preset changed over time renders every historical window at the dims it was
7716
- * written with.
7717
- */
7718
- var ScrubThumbnailPresetSchema = _enum([
7719
- "minimal",
7720
- "low",
7721
- "standard",
7722
- "high",
7723
- "max"
7724
- ]);
7725
- /**
7726
7718
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7727
7719
  *
7728
7720
  * `bands` is the ONLY authored recording intent: what to record, when, and on
@@ -7730,7 +7722,11 @@ var ScrubThumbnailPresetSchema = _enum([
7730
7722
  * other field is a storage knob (profiles, segment length, retention, scrub).
7731
7723
  *
7732
7724
  * STRICT on purpose: the legacy authoring surface (`schedule`/`schedules`/
7733
- * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30.
7725
+ * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30,
7726
+ * and `scrubThumbnails` — a five-step fidelity knob for a sprite tier that was
7727
+ * deleted on 2026-07-24 and had ZERO consumers in the recorder — on 2026-08-25
7728
+ * (D62: a switch that writes a store nobody reads is worse than no switch).
7729
+ * Stored rows keep loading: the READ schema is `.strip()` (config-store.ts).
7734
7730
  * A stale caller must fail loudly — silently stripping its legacy intent would
7735
7731
  * persist a band-less config, i.e. silently stop recording the camera.
7736
7732
  */
@@ -7753,14 +7749,7 @@ var RecordingConfigSchema = object({
7753
7749
  * "off" is the absence of a covering band, never a band value.
7754
7750
  */
7755
7751
  bands: array(RecordingBandSchema).default([]),
7756
- retention: RecordingRetentionSchema.optional(),
7757
- /**
7758
- * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7759
- * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7760
- * windows only — existing sheets are immutable, and each window's index
7761
- * carries its own tile dims so mixed-preset history renders correctly.
7762
- */
7763
- scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7752
+ retention: RecordingRetentionSchema.optional()
7764
7753
  }).strict();
7765
7754
  /**
7766
7755
  * Entity-relocation job state (storage entity-routing spec, Phase 4).
@@ -7836,10 +7825,11 @@ var RelocateFootageInputSchema = object({
7836
7825
  * `RecordingConfig.enabled` or camera wrapper bindings. */
7837
7826
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
7838
7827
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
7839
- var StorageMigrationMediaMoveInputSchema = object({
7828
+ var RelocateMediaInputSchema = object({
7840
7829
  toLocationId: string(),
7841
7830
  throttleMbps: number().min(1).max(1e3).optional()
7842
- }).extend({ leaseId: string().min(1) });
7831
+ });
7832
+ var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
7843
7833
  /** The independently selectable logical storage classes. `recordings`
7844
7834
  * encompasses the high and mid segment profiles; `recordingsLow` is low
7845
7835
  * segments; `eventMedia` is post-analysis blobs. */
@@ -8134,7 +8124,26 @@ var LabelDefinitionSchema = object({
8134
8124
  description: string().optional(),
8135
8125
  icon: string().optional()
8136
8126
  });
8137
- var ClassMapDefinitionSchema = object({
8127
+ /**
8128
+ * Wire schema for a per-model CATALOG classMap override
8129
+ * (`ModelCatalogEntry.classMap` / `ModelConvertMetadata.classMap`) —
8130
+ * restricted to {@link CLASS_MAP_MACRO_TARGETS}, the only macros the
8131
+ * detection pipeline executor actually routes.
8132
+ *
8133
+ * This is deliberately a DIFFERENT, narrower shape than the general-purpose
8134
+ * `ClassMapDefinition` interface above (e.g. `IDetectionAddon.getClassMap()`
8135
+ * and the audio `YAMNET_TO_MACRO` catalog both use macro targets outside this
8136
+ * enum) — the two used to share the name `ClassMapDefinition`/
8137
+ * `ClassMapDefinitionSchema`, which made the schema-type-twin guard
8138
+ * (`scripts/check-schema-type-twins.ts`) flag them as a duplicated shape. They
8139
+ * are not: it is two different concepts colliding on a name. Keep this type
8140
+ * under its own name rather than reusing `ClassMapDefinition` — reusing it
8141
+ * would either narrow every `ClassMapDefinition` consumer to the four
8142
+ * detection macros (breaking `YAMNET_TO_MACRO`) or drop the validation this
8143
+ * schema exists for (see the "rejects a classMap whose target is not a
8144
+ * detection macro" test in `model-catalog-schema.test.ts`).
8145
+ */
8146
+ var DetectionCatalogClassMapSchema = object({
8138
8147
  mapping: record(string(), _enum([
8139
8148
  "person",
8140
8149
  "vehicle",
@@ -8226,6 +8235,12 @@ var ModelVariantGroupSchema = object({
8226
8235
  */
8227
8236
  resolution: number().int().positive().optional()
8228
8237
  });
8238
+ var ModelProviderIdSchema = _enum([
8239
+ "camstack",
8240
+ "frigate",
8241
+ "scrypted",
8242
+ "custom"
8243
+ ]);
8229
8244
  var ModelCatalogEntrySchema = object({
8230
8245
  id: string(),
8231
8246
  name: string(),
@@ -8323,11 +8338,17 @@ var ModelCatalogEntrySchema = object({
8323
8338
  */
8324
8339
  group: ModelVariantGroupSchema.optional(),
8325
8340
  /**
8341
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8342
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8343
+ * persisted before this field existed (`inferModelProvider` fills those).
8344
+ */
8345
+ provider: ModelProviderIdSchema.optional(),
8346
+ /**
8326
8347
  * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8327
8348
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8328
8349
  * labels already ARE the CamStack macros (Scrypted identity map).
8329
8350
  */
8330
- classMap: ClassMapDefinitionSchema.optional()
8351
+ classMap: DetectionCatalogClassMapSchema.optional()
8331
8352
  });
8332
8353
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8333
8354
  format: literal("openvino"),
@@ -8357,7 +8378,7 @@ var ModelConvertMetadataSchema = object({
8357
8378
  "segmentation"
8358
8379
  ]),
8359
8380
  faceAlignment: boolean().optional(),
8360
- classMap: ClassMapDefinitionSchema.optional()
8381
+ classMap: DetectionCatalogClassMapSchema.optional()
8361
8382
  });
8362
8383
  var ConvertResultSchema = object({
8363
8384
  entry: ModelCatalogEntrySchema,
@@ -9220,7 +9241,7 @@ var AddonPageDeclarationSchema = object({
9220
9241
  /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
9221
9242
  sectionLabel: string().optional()
9222
9243
  });
9223
- method(_void(), array(AddonPageDeclarationSchema).readonly());
9244
+ method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
9224
9245
  var AddonHttpRouteSchema = object({
9225
9246
  method: _enum([
9226
9247
  "GET",
@@ -9479,7 +9500,20 @@ var addonWidgetsSourceCapability = {
9479
9500
  scope: "system",
9480
9501
  mode: "collection",
9481
9502
  internal: true,
9482
- methods: { listWidgets: method(_void(), array(WidgetMetadataSchema).readonly()) }
9503
+ methods: {
9504
+ /**
9505
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
9506
+ * `data-store-provider`) — this was reachable on the AppRouter by ANY
9507
+ * authenticated session at the default `auth: 'protected'`. The only
9508
+ * consumer, the `addon-widgets-aggregator` builtin, reads it via
9509
+ * `ctx.capabilities.getCollection('addon-widgets-source')` — LOCAL-PROCESS
9510
+ * only, never tRPC. The public, enriched listing admin-ui actually
9511
+ * consumes is the separate `addon-widgets` cap (`listWidgets` there is
9512
+ * gated separately, and a `preAuth: true` widget is surfaced through the
9513
+ * PUBLIC `auth.listLoginMethods` contribution channel instead) —
9514
+ * neither is affected by this change.
9515
+ */
9516
+ listWidgets: method(_void(), array(WidgetMetadataSchema).readonly(), { auth: "admin" }) }
9483
9517
  };
9484
9518
  /**
9485
9519
  * `addon-widgets` — system-scoped singleton aggregator cap. Public-facing
@@ -11015,7 +11049,7 @@ var CustomModelDescriptorSchema = object({
11015
11049
  stepId: string(),
11016
11050
  entry: ModelCatalogEntrySchema
11017
11051
  });
11018
- method(_void(), array(CustomModelDescriptorSchema).readonly());
11052
+ method(_void(), array(CustomModelDescriptorSchema).readonly(), { auth: "admin" });
11019
11053
  /**
11020
11054
  * Query filter for settings-store collections.
11021
11055
  */
@@ -11102,7 +11136,8 @@ method(object({
11102
11136
  }), _void(), { kind: "mutation" }), method(object({
11103
11137
  namespace: string().optional(),
11104
11138
  collection: string(),
11105
- filter: QueryFilterSchema.optional()
11139
+ filter: QueryFilterSchema.optional(),
11140
+ columns: array(string()).readonly().optional()
11106
11141
  }), array(SettingsRecordSchema).readonly()), method(object({
11107
11142
  namespace: string().optional(),
11108
11143
  collection: string(),
@@ -11165,46 +11200,87 @@ var EngineInfoSchema = object({
11165
11200
  kind: _enum(["relational", "vector"]),
11166
11201
  displayName: string()
11167
11202
  });
11168
- method(_void(), EngineInfoSchema), method(object({
11203
+ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
11169
11204
  namespace: string().optional(),
11170
11205
  collection: string(),
11171
11206
  key: string()
11172
- }), unknown()), method(object({
11207
+ }), unknown(), { auth: "admin" }), method(object({
11173
11208
  namespace: string().optional(),
11174
11209
  collection: string(),
11175
11210
  key: string(),
11176
11211
  value: unknown()
11177
- }), _void(), { kind: "mutation" }), method(object({
11212
+ }), _void(), {
11213
+ kind: "mutation",
11214
+ auth: "admin"
11215
+ }), method(object({
11178
11216
  namespace: string().optional(),
11179
11217
  collection: string(),
11180
- filter: QueryFilterSchema.optional()
11181
- }), array(SettingsRecordSchema).readonly()), method(object({
11218
+ filter: QueryFilterSchema.optional(),
11219
+ /**
11220
+ * SQL-level column projection — MUST mirror `settings-store.query`.
11221
+ *
11222
+ * ⚠ An earlier version of this comment blamed Zod stripping, and that
11223
+ * was wrong — corrected 2026-08-26 after the hop map
11224
+ * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
11225
+ * There is **no Zod parse at all** between the door and the engine: the
11226
+ * dispatcher forwards the payload verbatim and UDS carries it whole. A
11227
+ * field declared here reaches `SqliteSettingsBackend` either way.
11228
+ *
11229
+ * What actually lost `columns` was the THIRD declaration of this shape:
11230
+ * `SettingsQueryInput` in `interfaces/storage.ts`, a hand-written TS
11231
+ * interface the engine destructures from. The field existed on both
11232
+ * schemas and the engine still never read it, because nothing checks a
11233
+ * registered provider against `InferProvider<cap>` —
11234
+ * `ProviderRegistration.provider` is typed `object`.
11235
+ *
11236
+ * It is declared here anyway, and must stay in step with
11237
+ * `settings-store.query`: a caller reading only the cap definitions has
11238
+ * to be able to see that this call carries a projection.
11239
+ * `data-door-schema-parity.spec.ts` keeps the two aligned.
11240
+ */
11241
+ columns: array(string()).readonly().optional()
11242
+ }), array(SettingsRecordSchema).readonly(), { auth: "admin" }), method(object({
11182
11243
  namespace: string().optional(),
11183
11244
  collection: string(),
11184
11245
  record: SettingsRecordSchema
11185
- }), _void(), { kind: "mutation" }), method(object({
11246
+ }), _void(), {
11247
+ kind: "mutation",
11248
+ auth: "admin"
11249
+ }), method(object({
11186
11250
  namespace: string().optional(),
11187
11251
  collection: string(),
11188
11252
  id: string(),
11189
11253
  data: record(string(), unknown())
11190
- }), _void(), { kind: "mutation" }), method(object({
11254
+ }), _void(), {
11255
+ kind: "mutation",
11256
+ auth: "admin"
11257
+ }), method(object({
11191
11258
  namespace: string().optional(),
11192
11259
  collection: string(),
11193
11260
  key: string()
11194
- }), _void(), { kind: "mutation" }), method(object({
11261
+ }), _void(), {
11262
+ kind: "mutation",
11263
+ auth: "admin"
11264
+ }), method(object({
11195
11265
  namespace: string().optional(),
11196
11266
  collection: string(),
11197
11267
  filter: MutationFilterSchema
11198
- }), object({ deleted: number().int() }), { kind: "mutation" }), method(object({
11268
+ }), object({ deleted: number().int() }), {
11269
+ kind: "mutation",
11270
+ auth: "admin"
11271
+ }), method(object({
11199
11272
  namespace: string().optional(),
11200
11273
  collection: string(),
11201
11274
  filter: MutationFilterSchema,
11202
11275
  data: record(string(), unknown())
11203
- }), object({ updated: number().int() }), { kind: "mutation" }), method(object({
11276
+ }), object({ updated: number().int() }), {
11277
+ kind: "mutation",
11278
+ auth: "admin"
11279
+ }), method(object({
11204
11280
  namespace: string().optional(),
11205
11281
  collection: string(),
11206
11282
  filter: QueryFilterSchema.optional()
11207
- }), number()), method(object({
11283
+ }), number(), { auth: "admin" }), method(object({
11208
11284
  namespace: string().optional(),
11209
11285
  collection: string(),
11210
11286
  field: string(),
@@ -11214,15 +11290,18 @@ method(_void(), EngineInfoSchema), method(object({
11214
11290
  }), array(object({
11215
11291
  bucket: number().int(),
11216
11292
  count: number().int()
11217
- })).readonly()), method(object({
11293
+ })).readonly(), { auth: "admin" }), method(object({
11218
11294
  namespace: string().optional(),
11219
11295
  collection: string()
11220
- }), boolean()), method(object({
11296
+ }), boolean(), { auth: "admin" }), method(object({
11221
11297
  namespace: string().optional(),
11222
11298
  collection: string(),
11223
11299
  columns: array(CollectionColumnSchema).readonly(),
11224
11300
  indexes: array(CollectionIndexSchema).readonly().optional()
11225
- }), _void(), { kind: "mutation" });
11301
+ }), _void(), {
11302
+ kind: "mutation",
11303
+ auth: "admin"
11304
+ });
11226
11305
  /**
11227
11306
  * shm ring usage stats for a `frameSink: 'shm'` decoder session —
11228
11307
  * exposed via `decoder.getShmStats` so downstream consumers can
@@ -11888,6 +11967,27 @@ var LinkedDeviceSchema = object({
11888
11967
  features: array(string()),
11889
11968
  producesTrackedEvents: boolean().optional()
11890
11969
  });
11970
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
11971
+ * The batch answer needs the tag; the single-device answer already has it
11972
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
11973
+ var LinkedDevicesForDeviceSchema = object({
11974
+ deviceId: number(),
11975
+ mode: LinkedDevicesModeSchema,
11976
+ devices: array(LinkedDeviceSchema)
11977
+ });
11978
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
11979
+ * `getAllBindings` all answer in. Declared once: three copies of the same
11980
+ * object literal is exactly how the three drift apart. */
11981
+ var DeviceBindingsForDeviceSchema = object({
11982
+ deviceId: number(),
11983
+ entries: array(object({
11984
+ capName: string(),
11985
+ kind: _enum(["native", "wrapped"]),
11986
+ providerAddonId: string(),
11987
+ providerNodeId: string(),
11988
+ nativeAddonId: string()
11989
+ }))
11990
+ });
11891
11991
  var SavedDeviceRowSchema = object({
11892
11992
  /** Numeric id reserved at allocateDeviceId time. */
11893
11993
  id: number(),
@@ -12113,11 +12213,25 @@ method(object({
12113
12213
  projection: _enum(["full", "slim"]).optional(),
12114
12214
  /** Return only camera devices. Filtering server-side instead of
12115
12215
  * shipping 293 rows to find 12. */
12116
- isCamera: boolean().optional()
12216
+ isCamera: boolean().optional(),
12217
+ /**
12218
+ * Return only these device ids. For the caller that already KNOWS the
12219
+ * handful it wants and needs a field the id-bearing answer does not
12220
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12221
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12222
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12223
+ * refetches on the reconcile interval, on a phone.
12224
+ *
12225
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12226
+ * keys rather than rejecting them (verified against the live hub
12227
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12228
+ * it answers today and the caller filters as it already does.
12229
+ */
12230
+ deviceIds: array(number()).optional()
12117
12231
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12118
12232
  mode: LinkedDevicesModeSchema,
12119
12233
  devices: array(LinkedDeviceSchema)
12120
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12234
+ })), 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({
12121
12235
  deviceId: number(),
12122
12236
  values: record(string(), unknown())
12123
12237
  }), object({ success: literal(true) }), {
@@ -12144,25 +12258,7 @@ method(object({
12144
12258
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
12145
12259
  kind: "mutation",
12146
12260
  auth: "admin"
12147
- }), method(object({ deviceId: number() }), object({
12148
- deviceId: number(),
12149
- entries: array(object({
12150
- capName: string(),
12151
- kind: _enum(["native", "wrapped"]),
12152
- providerAddonId: string(),
12153
- providerNodeId: string(),
12154
- nativeAddonId: string()
12155
- }))
12156
- })), method(object({}), array(object({
12157
- deviceId: number(),
12158
- entries: array(object({
12159
- capName: string(),
12160
- kind: _enum(["native", "wrapped"]),
12161
- providerAddonId: string(),
12162
- providerNodeId: string(),
12163
- nativeAddonId: string()
12164
- }))
12165
- }))), method(object({
12261
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12166
12262
  deviceId: number(),
12167
12263
  capName: string(),
12168
12264
  wrapperAddonId: string(),
@@ -12333,7 +12429,7 @@ method(object({
12333
12429
  crop: _instanceof(Uint8Array),
12334
12430
  width: number(),
12335
12431
  height: number()
12336
- }), EmbeddingResultSchema), method(object({ text: string() }), EmbeddingResultSchema), method(_void(), EmbeddingInfoSchema);
12432
+ }), EmbeddingResultSchema, { auth: "admin" }), method(object({ text: string() }), EmbeddingResultSchema, { auth: "admin" }), method(_void(), EmbeddingInfoSchema, { auth: "admin" });
12337
12433
  /**
12338
12434
  * filesystem-browse — per-node capability for browsing the node's local
12339
12435
  * filesystem. Reads are unconfined (whole filesystem, from `/` down); WRITES
@@ -12626,19 +12722,22 @@ method(LlmGenerateBaseInputSchema.extend({
12626
12722
  runtime: ManagedRuntimeConfigSchema,
12627
12723
  /** The managed profile's timeout, threaded by the hub provider. */
12628
12724
  timeoutMs: number().int().positive().optional()
12629
- }), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
12725
+ }), LlmGenerateResultSchema, {
12726
+ kind: "mutation",
12727
+ auth: "admin"
12728
+ }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
12630
12729
  kind: "mutation",
12631
12730
  auth: "admin"
12632
12731
  }), method(object({}), _void(), {
12633
12732
  kind: "mutation",
12634
12733
  auth: "admin"
12635
- }), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
12734
+ }), method(object({}), LlmRuntimeStatusSchema, { auth: "admin" }), method(object({ model: ManagedModelRefSchema }), _void(), {
12636
12735
  kind: "mutation",
12637
12736
  auth: "admin"
12638
12737
  }), method(object({ file: string() }), _void(), {
12639
12738
  kind: "mutation",
12640
12739
  auth: "admin"
12641
- }), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
12740
+ }), method(object({}), array(LlmNodeModelSchema), { auth: "admin" }), method(object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" });
12642
12741
  /**
12643
12742
  * `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
12644
12743
  * methods concat-fan across providers; single-row methods route to ONE
@@ -14542,12 +14641,15 @@ var NcOccupancyConditionSchema = object({
14542
14641
  * there is no second switch that can disagree with the first and every rule
14543
14642
  * authored before the decision migrates for free (`audioModeOf`):
14544
14643
  *
14545
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14546
- * classifier labels with one of them. No window, no percentage:
14547
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14548
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14549
- * the analyzer's (`classificationMinScore`, per device) a label only
14550
- * reaches this condition if the classifier was already confident enough.
14644
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14645
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14646
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
14647
+ * frames is the wrong question for a classifier that labels 1–3 frames
14648
+ * per episode. The count window is the brake that drops a single-frame
14649
+ * false positive; the rule's own `throttle` cooldown is the other. The
14650
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14651
+ * per device) — a label only reaches this condition if the classifier was
14652
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14551
14653
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14552
14654
  * the condition: at least `hitPercent`% of the samples over
14553
14655
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14574,14 +14676,22 @@ var NcOccupancyConditionSchema = object({
14574
14676
  * an operator who typed `dog` mean the same thing.
14575
14677
  */
14576
14678
  var NcAudioConditionSchema = object({
14577
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14679
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14578
14680
  labels: array(string().min(1)).min(1).optional(),
14579
14681
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14580
14682
  dbThreshold: number().min(-96).max(0).optional(),
14581
14683
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14582
14684
  hitPercent: number().int().min(1).max(100).default(60),
14583
14685
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14584
- samplingSeconds: number().int().min(1).max(300).default(10)
14686
+ samplingSeconds: number().int().min(1).max(300).default(10),
14687
+ /**
14688
+ * LABEL MODE: how many labelled frames must land inside
14689
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
14690
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
14691
+ */
14692
+ confirmHits: number().int().min(1).max(20).optional(),
14693
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
14694
+ confirmWindowSec: number().int().min(1).max(60).optional()
14585
14695
  });
14586
14696
  /**
14587
14697
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -16108,7 +16218,7 @@ var OauthIntegrationDescriptorSchema = object({
16108
16218
  */
16109
16219
  refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
16110
16220
  });
16111
- method(_void(), OauthIntegrationDescriptorSchema);
16221
+ method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
16112
16222
  /**
16113
16223
  * pipeline-analytics — device-scoped wrapper cap. Refines raw
16114
16224
  * per-frame detections emitted by the pipeline runner into tracked
@@ -16955,6 +17065,46 @@ var RecentTracksPageSchema = object({
16955
17065
  /** Cursor for the next page, or null when this page is the last. */
16956
17066
  nextCursor: string().nullable()
16957
17067
  });
17068
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
17069
+ var LIST_GROUPS_MAX_LIMIT = 100;
17070
+ var AnalyticsGroupRecordSchema = object({
17071
+ id: string(),
17072
+ deviceId: number().int(),
17073
+ openedAt: number().int(),
17074
+ closedAt: number().int(),
17075
+ timestamp: number().int(),
17076
+ memberCount: number().int(),
17077
+ memberTrackIds: array(string()).readonly(),
17078
+ className: string(),
17079
+ classes: array(string()).readonly(),
17080
+ /** Relative event-media path, or null when the group has no picture yet. */
17081
+ mediaUrl: string().nullable(),
17082
+ singleton: boolean()
17083
+ });
17084
+ var AnalyticsGroupMemberSchema = object({
17085
+ trackId: string(),
17086
+ deviceId: number().int(),
17087
+ className: string(),
17088
+ firstSeen: number().int(),
17089
+ lastSeen: number().int(),
17090
+ mediaUrl: string().nullable()
17091
+ });
17092
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
17093
+ var ListGroupsQueryInput = object({
17094
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
17095
+ deviceIds: array(number()),
17096
+ /** Window lower bound on `closedAt` (inclusive). */
17097
+ since: number().optional(),
17098
+ /** Window upper bound on `openedAt` (inclusive). */
17099
+ until: number().optional(),
17100
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
17101
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
17102
+ cursor: string().optional()
17103
+ });
17104
+ var ListGroupsPageSchema = object({
17105
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
17106
+ nextCursor: string().nullable()
17107
+ });
16958
17108
  var KeyEventQueryInput = object({
16959
17109
  deviceId: number(),
16960
17110
  /** Window lower bound (track firstSeen ≥ since). */
@@ -17030,7 +17180,9 @@ var TrackCascadeCountsSchema = object({
17030
17180
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
17031
17181
  plates: number().int(),
17032
17182
  /** Per-track CLIP search vectors removed (best-effort). */
17033
- embeddings: number().int()
17183
+ embeddings: number().int(),
17184
+ /** Group membership + group rows removed with their last member (best-effort). */
17185
+ groups: number().int()
17034
17186
  });
17035
17187
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
17036
17188
  var DiskReconcileCountsSchema = object({
@@ -17176,7 +17328,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17176
17328
  * stationary registry). Default false: the timeline lists passages,
17177
17329
  * not parking records (operator decision, 2026-08-15). */
17178
17330
  includeStationary: boolean().optional()
17179
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17331
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17332
+ deviceId: number(),
17333
+ groupId: string().min(1)
17334
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17180
17335
  kind: "mutation",
17181
17336
  auth: "admin"
17182
17337
  }), 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({
@@ -17258,6 +17413,15 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17258
17413
  }), method(object({ jobId: string() }), RelocateJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
17259
17414
  kind: "mutation",
17260
17415
  auth: "admin"
17416
+ }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
17417
+ kind: "mutation",
17418
+ auth: "admin"
17419
+ }), method(object({}), array(RelocateJobSchema).readonly(), {
17420
+ kind: "query",
17421
+ auth: "admin"
17422
+ }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
17423
+ kind: "mutation",
17424
+ auth: "admin"
17261
17425
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
17262
17426
  kind: "query",
17263
17427
  auth: "admin"
@@ -17412,6 +17576,11 @@ object({
17412
17576
  "jpeg"
17413
17577
  ])
17414
17578
  });
17579
+ /**
17580
+ * Process-local frame identity. It is serializable so it can ride an in-process
17581
+ * capability call, but `registryId` deliberately prevents resolution in any
17582
+ * other process or execution group.
17583
+ */
17415
17584
  var FrameRefSchema = object({
17416
17585
  registryId: string().min(1),
17417
17586
  id: string().min(1),
@@ -17486,7 +17655,8 @@ var PipelineModelOptionSchema = object({
17486
17655
  sizeMB: number()
17487
17656
  })),
17488
17657
  group: ModelVariantGroupSchema.optional(),
17489
- legacy: boolean().optional()
17658
+ legacy: boolean().optional(),
17659
+ provider: ModelProviderIdSchema.optional()
17490
17660
  });
17491
17661
  var ConfigFieldBridge = custom();
17492
17662
  var PipelineAddonSchemaSchema = object({
@@ -19576,7 +19746,7 @@ method(object({
19576
19746
  * linking rather than produce an eternal token.
19577
19747
  */
19578
19748
  ttlSec: union([number().int().positive(), literal("never")]).optional()
19579
- }), object({ token: string() })), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable());
19749
+ }), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
19580
19750
  var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
19581
19751
  providerId: string().min(1),
19582
19752
  displayName: string().min(1),
@@ -19671,10 +19841,13 @@ var EvictResultSchema = object({
19671
19841
  /** True when the provider has nothing left it is willing to drop on this location. */
19672
19842
  exhausted: boolean()
19673
19843
  });
19674
- method(object({ locationId: string() }), EvictableUsageSchema), method(object({
19844
+ method(object({ locationId: string() }), EvictableUsageSchema, { auth: "admin" }), method(object({
19675
19845
  locationId: string(),
19676
19846
  targetBytes: number().int().positive()
19677
- }), EvictResultSchema, { kind: "mutation" });
19847
+ }), EvictResultSchema, {
19848
+ kind: "mutation",
19849
+ auth: "admin"
19850
+ });
19678
19851
  method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin" }), method(StorageMigrationInputSchema, object({ jobId: string() }), {
19679
19852
  kind: "mutation",
19680
19853
  auth: "admin"
@@ -19734,26 +19907,50 @@ var ReadChunkInputSchema = object({
19734
19907
  length: number()
19735
19908
  });
19736
19909
  var EndDownloadInputSchema = object({ downloadId: string() });
19737
- method(_void(), ProviderInfoSchema), method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }), method(object({
19910
+ method(_void(), ProviderInfoSchema, { auth: "admin" }), method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }), method(object({
19738
19911
  location: StorageLocationSchema,
19739
19912
  relativePath: string()
19740
- }), string()), method(object({
19913
+ }), string(), { auth: "admin" }), method(object({
19741
19914
  location: StorageLocationSchema,
19742
19915
  relativePath: string(),
19743
19916
  data: _instanceof(Uint8Array)
19744
- }), _void(), { kind: "mutation" }), method(object({
19917
+ }), _void(), {
19918
+ kind: "mutation",
19919
+ auth: "admin"
19920
+ }), method(object({
19745
19921
  location: StorageLocationSchema,
19746
19922
  relativePath: string()
19747
- }), _instanceof(Uint8Array)), method(object({
19923
+ }), _instanceof(Uint8Array), { auth: "admin" }), method(object({
19748
19924
  location: StorageLocationSchema,
19749
19925
  relativePath: string()
19750
- }), boolean()), method(object({
19926
+ }), boolean(), { auth: "admin" }), method(object({
19751
19927
  location: StorageLocationSchema,
19752
19928
  prefix: string().optional()
19753
- }), array(string()).readonly()), method(object({
19929
+ }), array(string()).readonly(), { auth: "admin" }), method(object({
19754
19930
  location: StorageLocationSchema,
19755
19931
  relativePath: string()
19756
- }), _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" });
19932
+ }), _void(), {
19933
+ kind: "mutation",
19934
+ auth: "admin"
19935
+ }), method(object({ location: StorageLocationSchema }), number().nullable(), { auth: "admin" }), method(BeginUploadInputSchema, BeginUploadResultSchema, {
19936
+ kind: "mutation",
19937
+ auth: "admin"
19938
+ }), method(WriteChunkInputSchema, _void(), {
19939
+ kind: "mutation",
19940
+ auth: "admin"
19941
+ }), method(FinalizeUploadInputSchema, _void(), {
19942
+ kind: "mutation",
19943
+ auth: "admin"
19944
+ }), method(AbortUploadInputSchema, _void(), {
19945
+ kind: "mutation",
19946
+ auth: "admin"
19947
+ }), method(BeginDownloadInputSchema, BeginDownloadResultSchema, {
19948
+ kind: "mutation",
19949
+ auth: "admin"
19950
+ }), method(ReadChunkInputSchema, _instanceof(Uint8Array), { auth: "admin" }), method(EndDownloadInputSchema, _void(), {
19951
+ kind: "mutation",
19952
+ auth: "admin"
19953
+ });
19757
19954
  /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
19758
19955
  var ProfileSettingsSchemaBridge = unknown().nullable();
19759
19956
  var ProfileSettingsBagSchema = record(string(), unknown());
@@ -20017,9 +20214,28 @@ var userPasskeysCapability = {
20017
20214
  auth: "admin",
20018
20215
  access: "create"
20019
20216
  }),
20217
+ /**
20218
+ * The four methods below had NO `auth` override — `internal: true` did
20219
+ * not gate the mount (see the 2026-08-26 note on `data-store-provider`),
20220
+ * so they were reachable on the AppRouter at the default
20221
+ * `auth: 'protected'` (any authenticated session), inconsistent with
20222
+ * every other method on this cap (all already `auth: 'admin'`).
20223
+ *
20224
+ * ⚠ Checked before gating: these are also the PRE-AUTH login-ceremony
20225
+ * methods, so admin-gating them here must not break login for an
20226
+ * unauthenticated principal. It doesn't — the real login path is the
20227
+ * PUBLIC `auth.beginAuthentication` / `auth.finishAuthentication` /
20228
+ * `auth.beginDiscoverableAuthentication` / `auth.finishDiscoverableAuthentication`
20229
+ * procedures in `server/backend/src/api/core/auth.router.ts`, which call
20230
+ * `provider.beginAuthentication(...)` etc. directly against the
20231
+ * capability-registry-resolved provider — never through this cap's own
20232
+ * tRPC route. `auth: 'admin'` here only closes a redundant, unused
20233
+ * second entry point onto the same ceremony.
20234
+ */
20020
20235
  beginAuthentication: method(object({ userId: string().optional() }), object({ optionsJSON: record(string(), unknown()) }), {
20021
20236
  kind: "mutation",
20022
- access: "view"
20237
+ access: "view",
20238
+ auth: "admin"
20023
20239
  }),
20024
20240
  finishAuthentication: method(object({
20025
20241
  /** Required — the user the assertion belongs to (verified). */
@@ -20028,11 +20244,13 @@ var userPasskeysCapability = {
20028
20244
  response: record(string(), unknown())
20029
20245
  }), object({ verified: boolean() }), {
20030
20246
  kind: "mutation",
20031
- access: "view"
20247
+ access: "view",
20248
+ auth: "admin"
20032
20249
  }),
20033
20250
  beginDiscoverableAuthentication: method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
20034
20251
  kind: "mutation",
20035
- access: "view"
20252
+ access: "view",
20253
+ auth: "admin"
20036
20254
  }),
20037
20255
  finishDiscoverableAuthentication: method(object({
20038
20256
  /** AuthenticationResponseJSON from the browser. */
@@ -20041,7 +20259,8 @@ response: record(string(), unknown()) }), object({
20041
20259
  userId: string().nullable()
20042
20260
  }), {
20043
20261
  kind: "mutation",
20044
- access: "view"
20262
+ access: "view",
20263
+ auth: "admin"
20045
20264
  }),
20046
20265
  listPasskeys: method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }),
20047
20266
  removePasskey: method(object({
@@ -20219,7 +20438,19 @@ var VectorStatsResultSchema = object({
20219
20438
  /** False when the backend ranks approximately. */
20220
20439
  exact: boolean()
20221
20440
  });
20222
- 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);
20441
+ method(VectorDeclareIndexInputSchema, _void(), {
20442
+ kind: "mutation",
20443
+ auth: "admin"
20444
+ }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
20445
+ kind: "mutation",
20446
+ auth: "admin"
20447
+ }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
20448
+ kind: "mutation",
20449
+ auth: "admin"
20450
+ }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
20451
+ kind: "mutation",
20452
+ auth: "admin"
20453
+ }), method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" });
20223
20454
  var ClipSchema = object({
20224
20455
  /** Opaque, provider-namespaced id. The default provider encodes the time
20225
20456
  * window so `getClipPlayback` is self-contained (no event re-query). */
@@ -21942,7 +22173,27 @@ var MediaFileLiteSchema$1 = object({
21942
22173
  sizeBytes: number(),
21943
22174
  timestamp: number()
21944
22175
  });
21945
- method(_void(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
22176
+ method(object({
22177
+ /**
22178
+ * Inline {@link IdentitySchema.coverBase64} on every row.
22179
+ *
22180
+ * Default `false`, the same inversion `listRecentFaces` and
22181
+ * `listPlates` took on 2026-08-25 (see `include-crops-default.ts` for
22182
+ * why the burden belongs on the caller that WANTS the bytes). Measured
22183
+ * on the live hub the same day: four identities cost 40,979 B with the
22184
+ * covers inline, ~10 KB of base64 per row, on a query this UI mounts
22185
+ * four times and the viewer holds at `staleTime: 30_000`.
22186
+ *
22187
+ * Nothing loses its avatar: `coverMediaKey` is already on every row and
22188
+ * the `event-media` plane serves that key `immutable` with an ETag.
22189
+ *
22190
+ * **This is an INPUT field, so it does not reach the addon until the
22191
+ * next train** — the hub router validates cap inputs against its own
22192
+ * compiled Zod and strips a key it does not know. Until then the
22193
+ * provider sees `undefined`, which resolves to `false`: the cheap shape
22194
+ * is what ships, and the opt-in becomes reachable when the train lands.
22195
+ */
22196
+ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
21946
22197
  kind: "mutation",
21947
22198
  auth: "admin"
21948
22199
  }), method(object({
@@ -24087,7 +24338,14 @@ var PlateInfoSchema = object({
24087
24338
  plateBbox: BoundingBoxSchema.optional(),
24088
24339
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
24089
24340
  keyFrameMediaKey: string().optional(),
24090
- base64: string().optional()
24341
+ base64: string().optional(),
24342
+ /**
24343
+ * Same crop as a data-plane URL, always present when the plate has a stored
24344
+ * crop. `getPlateByTrack` returns this and never inlines JPEG; `listPlates`
24345
+ * and `searchPlates` inline the crop only when their `includeCrops` input is
24346
+ * left at its `true` default.
24347
+ */
24348
+ cropUrl: string().optional()
24091
24349
  });
24092
24350
  var MediaFileLiteSchema = object({
24093
24351
  key: string(),
@@ -24105,14 +24363,34 @@ var PlateClusterSchema = object({
24105
24363
  });
24106
24364
  method(object({
24107
24365
  deviceId: number().int().optional(),
24108
- limit: number().int().positive().optional()
24366
+ limit: number().int().positive().optional(),
24367
+ /**
24368
+ * Inline the base64 crop on every row. Default `true` — the existing
24369
+ * behaviour, kept so no caller breaks.
24370
+ *
24371
+ * Set `false` once the caller renders {@link PlateInfo.cropUrl}.
24372
+ * Measured on the live hub at the 500 rows the Plates view asks for:
24373
+ * 879,403 B and 27.7 s with the crops inline, against a few KiB of
24374
+ * metadata without them — and the browser then caches the images.
24375
+ *
24376
+ * This is the plate twin of `faceGallery.listRecentFaces`'s option;
24377
+ * plates were the one gallery list left without it.
24378
+ *
24379
+ * **This is an INPUT field, so it does not reach the addon until the
24380
+ * next train.** The hub router validates cap inputs against its own
24381
+ * compiled Zod and strips a key it does not know. Until the train
24382
+ * ships, sending `false` is harmless and keeps the crops inline.
24383
+ */
24384
+ includeCrops: boolean().optional()
24109
24385
  }).optional(), array(PlateInfoSchema).readonly()), method(object({
24110
24386
  deviceId: number().int(),
24111
24387
  trackId: string()
24112
24388
  }), PlateInfoSchema.nullable()), method(object({ plateId: string() }), array(MediaFileLiteSchema).readonly()), method(object({
24113
24389
  text: string().min(1),
24114
24390
  maxDistance: number().int().min(0).optional(),
24115
- limit: number().int().positive().optional()
24391
+ limit: number().int().positive().optional(),
24392
+ /** See `listPlates.includeCrops`. Default `true`. */
24393
+ includeCrops: boolean().optional()
24116
24394
  }), array(PlateInfoSchema).readonly()), method(object({
24117
24395
  maxDistance: number().int().min(0).optional(),
24118
24396
  minClusterSize: number().int().min(2).optional(),
@@ -24126,7 +24404,13 @@ method(object({
24126
24404
  }), method(object({ plateId: string() }), _void(), {
24127
24405
  kind: "mutation",
24128
24406
  auth: "admin"
24129
- }), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
24407
+ }), method(object({
24408
+ /** Inline {@link VehicleSchema.coverBase64} on every row. Default
24409
+ * `false` — the vehicle twin of `faceGallery.listIdentities`'s
24410
+ * option; `coverMediaKey` + the `event-media` plane carry the picture.
24411
+ * INPUT field: stripped by the hub router until the train ships, which
24412
+ * resolves to `false` and is exactly the intended default. */
24413
+ includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
24130
24414
  kind: "mutation",
24131
24415
  auth: "admin"
24132
24416
  }), method(object({
@@ -27727,6 +28011,12 @@ Object.freeze({
27727
28011
  addonId: null,
27728
28012
  access: "view"
27729
28013
  },
28014
+ "deviceManager.getBindingsBatch": {
28015
+ capName: "device-manager",
28016
+ capScope: "system",
28017
+ addonId: null,
28018
+ access: "view"
28019
+ },
27730
28020
  "deviceManager.getChildren": {
27731
28021
  capName: "device-manager",
27732
28022
  capScope: "system",
@@ -27787,6 +28077,12 @@ Object.freeze({
27787
28077
  addonId: null,
27788
28078
  access: "view"
27789
28079
  },
28080
+ "deviceManager.getLinkedDevicesBatch": {
28081
+ capName: "device-manager",
28082
+ capScope: "system",
28083
+ addonId: null,
28084
+ access: "view"
28085
+ },
27790
28086
  "deviceManager.getRoleDisplayDefaults": {
27791
28087
  capName: "device-manager",
27792
28088
  capScope: "system",
@@ -29491,6 +29787,12 @@ Object.freeze({
29491
29787
  addonId: null,
29492
29788
  access: "create"
29493
29789
  },
29790
+ "pipelineAnalytics.cancelRelocateMedia": {
29791
+ capName: "pipeline-analytics",
29792
+ capScope: "device",
29793
+ addonId: null,
29794
+ access: "create"
29795
+ },
29494
29796
  "pipelineAnalytics.cancelStorageMigrationMove": {
29495
29797
  capName: "pipeline-analytics",
29496
29798
  capScope: "device",
@@ -29557,6 +29859,12 @@ Object.freeze({
29557
29859
  addonId: null,
29558
29860
  access: "view"
29559
29861
  },
29862
+ "pipelineAnalytics.getGroup": {
29863
+ capName: "pipeline-analytics",
29864
+ capScope: "device",
29865
+ addonId: null,
29866
+ access: "view"
29867
+ },
29560
29868
  "pipelineAnalytics.getKeyEvents": {
29561
29869
  capName: "pipeline-analytics",
29562
29870
  capScope: "device",
@@ -29641,6 +29949,12 @@ Object.freeze({
29641
29949
  addonId: null,
29642
29950
  access: "view"
29643
29951
  },
29952
+ "pipelineAnalytics.listGroups": {
29953
+ capName: "pipeline-analytics",
29954
+ capScope: "device",
29955
+ addonId: null,
29956
+ access: "view"
29957
+ },
29644
29958
  "pipelineAnalytics.listOpsLog": {
29645
29959
  capName: "pipeline-analytics",
29646
29960
  capScope: "device",
@@ -29653,6 +29967,12 @@ Object.freeze({
29653
29967
  addonId: null,
29654
29968
  access: "view"
29655
29969
  },
29970
+ "pipelineAnalytics.listRelocateMediaJobs": {
29971
+ capName: "pipeline-analytics",
29972
+ capScope: "device",
29973
+ addonId: null,
29974
+ access: "view"
29975
+ },
29656
29976
  "pipelineAnalytics.listRetrainAnnotations": {
29657
29977
  capName: "pipeline-analytics",
29658
29978
  capScope: "device",
@@ -29731,6 +30051,12 @@ Object.freeze({
29731
30051
  addonId: null,
29732
30052
  access: "create"
29733
30053
  },
30054
+ "pipelineAnalytics.relocateMedia": {
30055
+ capName: "pipeline-analytics",
30056
+ capScope: "device",
30057
+ addonId: null,
30058
+ access: "create"
30059
+ },
29734
30060
  "pipelineAnalytics.restageRetrainTrack": {
29735
30061
  capName: "pipeline-analytics",
29736
30062
  capScope: "device",
@@ -32422,6 +32748,11 @@ Object.freeze({
32422
32748
  form: "single",
32423
32749
  optional: false
32424
32750
  }],
32751
+ "deviceManager.getBindingsBatch": [{
32752
+ name: "deviceIds",
32753
+ form: "array",
32754
+ optional: false
32755
+ }],
32425
32756
  "deviceManager.getChildren": [{
32426
32757
  name: "parentDeviceId",
32427
32758
  form: "single",
@@ -32467,6 +32798,11 @@ Object.freeze({
32467
32798
  form: "single",
32468
32799
  optional: false
32469
32800
  }],
32801
+ "deviceManager.getLinkedDevicesBatch": [{
32802
+ name: "deviceIds",
32803
+ form: "array",
32804
+ optional: false
32805
+ }],
32470
32806
  "deviceManager.getSettingsSchema": [{
32471
32807
  name: "deviceId",
32472
32808
  form: "single",
@@ -32487,6 +32823,11 @@ Object.freeze({
32487
32823
  form: "single",
32488
32824
  optional: false
32489
32825
  }],
32826
+ "deviceManager.listAll": [{
32827
+ name: "deviceIds",
32828
+ form: "array",
32829
+ optional: true
32830
+ }],
32490
32831
  "deviceManager.loadConfig": [{
32491
32832
  name: "deviceId",
32492
32833
  form: "single",
@@ -33060,6 +33401,11 @@ Object.freeze({
33060
33401
  form: "single",
33061
33402
  optional: false
33062
33403
  }],
33404
+ "pipelineAnalytics.getGroup": [{
33405
+ name: "deviceId",
33406
+ form: "single",
33407
+ optional: false
33408
+ }],
33063
33409
  "pipelineAnalytics.getKeyEvents": [{
33064
33410
  name: "deviceId",
33065
33411
  form: "single",
@@ -33115,6 +33461,11 @@ Object.freeze({
33115
33461
  form: "array",
33116
33462
  optional: false
33117
33463
  }],
33464
+ "pipelineAnalytics.listGroups": [{
33465
+ name: "deviceIds",
33466
+ form: "array",
33467
+ optional: false
33468
+ }],
33118
33469
  "pipelineAnalytics.listOpsLog": [{
33119
33470
  name: "deviceId",
33120
33471
  form: "single",