@camstack/addon-remote-storage 1.2.26 → 1.2.28

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.
@@ -5802,6 +5802,13 @@ var BaseAddon = class {
5802
5802
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5803
5803
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5804
5804
  _registeredCapNames = [];
5805
+ /**
5806
+ * True only after `readAddonStore` actually answered. Constructor
5807
+ * defaults look like stored config when the store is down — a forked
5808
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5809
+ * mode, 2026-08-25) is not "the operator chose this".
5810
+ */
5811
+ settingsStoreReady = false;
5805
5812
  /** Default config values. Provided via constructor. */
5806
5813
  defaults;
5807
5814
  constructor(defaults) {
@@ -6202,7 +6209,9 @@ var BaseAddon = class {
6202
6209
  ];
6203
6210
  let lastErr;
6204
6211
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6205
- return await settings.readAddonStore() ?? {};
6212
+ const stored = await settings.readAddonStore() ?? {};
6213
+ this.settingsStoreReady = true;
6214
+ return stored;
6206
6215
  } catch (err) {
6207
6216
  lastErr = err;
6208
6217
  const msg = err instanceof Error ? err.message : String(err);
@@ -6210,6 +6219,7 @@ var BaseAddon = class {
6210
6219
  if (attempt === delaysMs.length) break;
6211
6220
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6212
6221
  }
6222
+ this.settingsStoreReady = false;
6213
6223
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6214
6224
  return {};
6215
6225
  }
@@ -6621,7 +6631,7 @@ function method(input, output, options) {
6621
6631
  input,
6622
6632
  output,
6623
6633
  kind: options?.kind ?? "query",
6624
- auth: options?.auth ?? "protected",
6634
+ ...options?.auth !== void 0 ? { auth: options.auth } : {},
6625
6635
  ...options?.access !== void 0 ? { access: options.access } : {},
6626
6636
  ...options?.caller !== void 0 ? { caller: options.caller } : {},
6627
6637
  timeoutMs: options?.timeoutMs
@@ -6641,7 +6651,7 @@ function systemMethod(input, output, options) {
6641
6651
  }
6642
6652
  var StaticDirOutputSchema$1 = object({ staticDir: string() });
6643
6653
  var VersionOutputSchema$1 = object({ version: string() });
6644
- method(_void(), StaticDirOutputSchema$1), method(_void(), VersionOutputSchema$1);
6654
+ method(_void(), StaticDirOutputSchema$1, { auth: "admin" }), method(_void(), VersionOutputSchema$1, { auth: "admin" });
6645
6655
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
6646
6656
  DeviceType["Camera"] = "camera";
6647
6657
  DeviceType["Hub"] = "hub";
@@ -6962,7 +6972,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
6962
6972
  }({});
6963
6973
  var StaticDirOutputSchema = object({ staticDir: string() });
6964
6974
  var VersionOutputSchema = object({ version: string() });
6965
- method(_void(), StaticDirOutputSchema), method(_void(), VersionOutputSchema);
6975
+ method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
6966
6976
  /**
6967
6977
  * device-ops — device-scoped cap that unifies the per-IDevice operations
6968
6978
  * previously routed through the `.device-ops` Moleculer bridge service.
@@ -7576,24 +7586,6 @@ var RecordingRetentionSchema = object({
7576
7586
  maxSizeGb: number().min(0).optional()
7577
7587
  });
7578
7588
  /**
7579
- * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7580
- * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7581
- * previews at. Five graduated steps; absent on a config = `standard` (the
7582
- * shipped default, matching `sheet-geometry`/`sheet-composer`).
7583
- *
7584
- * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7585
- * Each window's index sidecar carries its own tile dims, so a camera whose
7586
- * preset changed over time renders every historical window at the dims it was
7587
- * written with.
7588
- */
7589
- var ScrubThumbnailPresetSchema = _enum([
7590
- "minimal",
7591
- "low",
7592
- "standard",
7593
- "high",
7594
- "max"
7595
- ]);
7596
- /**
7597
7589
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7598
7590
  *
7599
7591
  * `bands` is the ONLY authored recording intent: what to record, when, and on
@@ -7601,7 +7593,11 @@ var ScrubThumbnailPresetSchema = _enum([
7601
7593
  * other field is a storage knob (profiles, segment length, retention, scrub).
7602
7594
  *
7603
7595
  * STRICT on purpose: the legacy authoring surface (`schedule`/`schedules`/
7604
- * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30.
7596
+ * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30,
7597
+ * and `scrubThumbnails` — a five-step fidelity knob for a sprite tier that was
7598
+ * deleted on 2026-07-24 and had ZERO consumers in the recorder — on 2026-08-25
7599
+ * (D62: a switch that writes a store nobody reads is worse than no switch).
7600
+ * Stored rows keep loading: the READ schema is `.strip()` (config-store.ts).
7605
7601
  * A stale caller must fail loudly — silently stripping its legacy intent would
7606
7602
  * persist a band-less config, i.e. silently stop recording the camera.
7607
7603
  */
@@ -7624,14 +7620,7 @@ var RecordingConfigSchema = object({
7624
7620
  * "off" is the absence of a covering band, never a band value.
7625
7621
  */
7626
7622
  bands: array(RecordingBandSchema).default([]),
7627
- retention: RecordingRetentionSchema.optional(),
7628
- /**
7629
- * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7630
- * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7631
- * windows only — existing sheets are immutable, and each window's index
7632
- * carries its own tile dims so mixed-preset history renders correctly.
7633
- */
7634
- scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7623
+ retention: RecordingRetentionSchema.optional()
7635
7624
  }).strict();
7636
7625
  /**
7637
7626
  * Entity-relocation job state (storage entity-routing spec, Phase 4).
@@ -7707,10 +7696,11 @@ var RelocateFootageInputSchema = object({
7707
7696
  * `RecordingConfig.enabled` or camera wrapper bindings. */
7708
7697
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
7709
7698
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
7710
- var StorageMigrationMediaMoveInputSchema = object({
7699
+ var RelocateMediaInputSchema = object({
7711
7700
  toLocationId: string(),
7712
7701
  throttleMbps: number().min(1).max(1e3).optional()
7713
- }).extend({ leaseId: string().min(1) });
7702
+ });
7703
+ var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
7714
7704
  /** The independently selectable logical storage classes. `recordings`
7715
7705
  * encompasses the high and mid segment profiles; `recordingsLow` is low
7716
7706
  * segments; `eventMedia` is post-analysis blobs. */
@@ -8005,7 +7995,26 @@ var LabelDefinitionSchema = object({
8005
7995
  description: string().optional(),
8006
7996
  icon: string().optional()
8007
7997
  });
8008
- var ClassMapDefinitionSchema = object({
7998
+ /**
7999
+ * Wire schema for a per-model CATALOG classMap override
8000
+ * (`ModelCatalogEntry.classMap` / `ModelConvertMetadata.classMap`) —
8001
+ * restricted to {@link CLASS_MAP_MACRO_TARGETS}, the only macros the
8002
+ * detection pipeline executor actually routes.
8003
+ *
8004
+ * This is deliberately a DIFFERENT, narrower shape than the general-purpose
8005
+ * `ClassMapDefinition` interface above (e.g. `IDetectionAddon.getClassMap()`
8006
+ * and the audio `YAMNET_TO_MACRO` catalog both use macro targets outside this
8007
+ * enum) — the two used to share the name `ClassMapDefinition`/
8008
+ * `ClassMapDefinitionSchema`, which made the schema-type-twin guard
8009
+ * (`scripts/check-schema-type-twins.ts`) flag them as a duplicated shape. They
8010
+ * are not: it is two different concepts colliding on a name. Keep this type
8011
+ * under its own name rather than reusing `ClassMapDefinition` — reusing it
8012
+ * would either narrow every `ClassMapDefinition` consumer to the four
8013
+ * detection macros (breaking `YAMNET_TO_MACRO`) or drop the validation this
8014
+ * schema exists for (see the "rejects a classMap whose target is not a
8015
+ * detection macro" test in `model-catalog-schema.test.ts`).
8016
+ */
8017
+ var DetectionCatalogClassMapSchema = object({
8009
8018
  mapping: record(string(), _enum([
8010
8019
  "person",
8011
8020
  "vehicle",
@@ -8097,6 +8106,12 @@ var ModelVariantGroupSchema = object({
8097
8106
  */
8098
8107
  resolution: number().int().positive().optional()
8099
8108
  });
8109
+ var ModelProviderIdSchema = _enum([
8110
+ "camstack",
8111
+ "frigate",
8112
+ "scrypted",
8113
+ "custom"
8114
+ ]);
8100
8115
  var ModelCatalogEntrySchema = object({
8101
8116
  id: string(),
8102
8117
  name: string(),
@@ -8194,11 +8209,17 @@ var ModelCatalogEntrySchema = object({
8194
8209
  */
8195
8210
  group: ModelVariantGroupSchema.optional(),
8196
8211
  /**
8212
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8213
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8214
+ * persisted before this field existed (`inferModelProvider` fills those).
8215
+ */
8216
+ provider: ModelProviderIdSchema.optional(),
8217
+ /**
8197
8218
  * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8198
8219
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8199
8220
  * labels already ARE the CamStack macros (Scrypted identity map).
8200
8221
  */
8201
- classMap: ClassMapDefinitionSchema.optional()
8222
+ classMap: DetectionCatalogClassMapSchema.optional()
8202
8223
  });
8203
8224
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8204
8225
  format: literal("openvino"),
@@ -8228,7 +8249,7 @@ var ModelConvertMetadataSchema = object({
8228
8249
  "segmentation"
8229
8250
  ]),
8230
8251
  faceAlignment: boolean().optional(),
8231
- classMap: ClassMapDefinitionSchema.optional()
8252
+ classMap: DetectionCatalogClassMapSchema.optional()
8232
8253
  });
8233
8254
  var ConvertResultSchema = object({
8234
8255
  entry: ModelCatalogEntrySchema,
@@ -9091,7 +9112,7 @@ var AddonPageDeclarationSchema = object({
9091
9112
  /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
9092
9113
  sectionLabel: string().optional()
9093
9114
  });
9094
- method(_void(), array(AddonPageDeclarationSchema).readonly());
9115
+ method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
9095
9116
  var AddonHttpRouteSchema = object({
9096
9117
  method: _enum([
9097
9118
  "GET",
@@ -9326,7 +9347,7 @@ var WidgetMetadataSchema = object({
9326
9347
  defaultColumns: number().int().min(1).max(12).default(6),
9327
9348
  defaultRows: number().int().min(1).max(12).default(1)
9328
9349
  });
9329
- method(_void(), array(WidgetMetadataSchema).readonly());
9350
+ method(_void(), array(WidgetMetadataSchema).readonly(), { auth: "admin" });
9330
9351
  /**
9331
9352
  * `addon-widgets` — system-scoped singleton aggregator cap. Public-facing
9332
9353
  * surface that admin-ui consumes through `useAddonWidgetsListWidgets()`.
@@ -10848,7 +10869,7 @@ var CustomModelDescriptorSchema = object({
10848
10869
  stepId: string(),
10849
10870
  entry: ModelCatalogEntrySchema
10850
10871
  });
10851
- method(_void(), array(CustomModelDescriptorSchema).readonly());
10872
+ method(_void(), array(CustomModelDescriptorSchema).readonly(), { auth: "admin" });
10852
10873
  /**
10853
10874
  * Query filter for settings-store collections.
10854
10875
  */
@@ -10935,7 +10956,8 @@ method(object({
10935
10956
  }), _void(), { kind: "mutation" }), method(object({
10936
10957
  namespace: string().optional(),
10937
10958
  collection: string(),
10938
- filter: QueryFilterSchema.optional()
10959
+ filter: QueryFilterSchema.optional(),
10960
+ columns: array(string()).readonly().optional()
10939
10961
  }), array(SettingsRecordSchema).readonly()), method(object({
10940
10962
  namespace: string().optional(),
10941
10963
  collection: string(),
@@ -10998,46 +11020,87 @@ var EngineInfoSchema = object({
10998
11020
  kind: _enum(["relational", "vector"]),
10999
11021
  displayName: string()
11000
11022
  });
11001
- method(_void(), EngineInfoSchema), method(object({
11023
+ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
11002
11024
  namespace: string().optional(),
11003
11025
  collection: string(),
11004
11026
  key: string()
11005
- }), unknown()), method(object({
11027
+ }), unknown(), { auth: "admin" }), method(object({
11006
11028
  namespace: string().optional(),
11007
11029
  collection: string(),
11008
11030
  key: string(),
11009
11031
  value: unknown()
11010
- }), _void(), { kind: "mutation" }), method(object({
11032
+ }), _void(), {
11033
+ kind: "mutation",
11034
+ auth: "admin"
11035
+ }), method(object({
11011
11036
  namespace: string().optional(),
11012
11037
  collection: string(),
11013
- filter: QueryFilterSchema.optional()
11014
- }), array(SettingsRecordSchema).readonly()), method(object({
11038
+ filter: QueryFilterSchema.optional(),
11039
+ /**
11040
+ * SQL-level column projection — MUST mirror `settings-store.query`.
11041
+ *
11042
+ * ⚠ An earlier version of this comment blamed Zod stripping, and that
11043
+ * was wrong — corrected 2026-08-26 after the hop map
11044
+ * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
11045
+ * There is **no Zod parse at all** between the door and the engine: the
11046
+ * dispatcher forwards the payload verbatim and UDS carries it whole. A
11047
+ * field declared here reaches `SqliteSettingsBackend` either way.
11048
+ *
11049
+ * What actually lost `columns` was the THIRD declaration of this shape:
11050
+ * `SettingsQueryInput` in `interfaces/storage.ts`, a hand-written TS
11051
+ * interface the engine destructures from. The field existed on both
11052
+ * schemas and the engine still never read it, because nothing checks a
11053
+ * registered provider against `InferProvider<cap>` —
11054
+ * `ProviderRegistration.provider` is typed `object`.
11055
+ *
11056
+ * It is declared here anyway, and must stay in step with
11057
+ * `settings-store.query`: a caller reading only the cap definitions has
11058
+ * to be able to see that this call carries a projection.
11059
+ * `data-door-schema-parity.spec.ts` keeps the two aligned.
11060
+ */
11061
+ columns: array(string()).readonly().optional()
11062
+ }), array(SettingsRecordSchema).readonly(), { auth: "admin" }), method(object({
11015
11063
  namespace: string().optional(),
11016
11064
  collection: string(),
11017
11065
  record: SettingsRecordSchema
11018
- }), _void(), { kind: "mutation" }), method(object({
11066
+ }), _void(), {
11067
+ kind: "mutation",
11068
+ auth: "admin"
11069
+ }), method(object({
11019
11070
  namespace: string().optional(),
11020
11071
  collection: string(),
11021
11072
  id: string(),
11022
11073
  data: record(string(), unknown())
11023
- }), _void(), { kind: "mutation" }), method(object({
11074
+ }), _void(), {
11075
+ kind: "mutation",
11076
+ auth: "admin"
11077
+ }), method(object({
11024
11078
  namespace: string().optional(),
11025
11079
  collection: string(),
11026
11080
  key: string()
11027
- }), _void(), { kind: "mutation" }), method(object({
11081
+ }), _void(), {
11082
+ kind: "mutation",
11083
+ auth: "admin"
11084
+ }), method(object({
11028
11085
  namespace: string().optional(),
11029
11086
  collection: string(),
11030
11087
  filter: MutationFilterSchema
11031
- }), object({ deleted: number().int() }), { kind: "mutation" }), method(object({
11088
+ }), object({ deleted: number().int() }), {
11089
+ kind: "mutation",
11090
+ auth: "admin"
11091
+ }), method(object({
11032
11092
  namespace: string().optional(),
11033
11093
  collection: string(),
11034
11094
  filter: MutationFilterSchema,
11035
11095
  data: record(string(), unknown())
11036
- }), object({ updated: number().int() }), { kind: "mutation" }), method(object({
11096
+ }), object({ updated: number().int() }), {
11097
+ kind: "mutation",
11098
+ auth: "admin"
11099
+ }), method(object({
11037
11100
  namespace: string().optional(),
11038
11101
  collection: string(),
11039
11102
  filter: QueryFilterSchema.optional()
11040
- }), number()), method(object({
11103
+ }), number(), { auth: "admin" }), method(object({
11041
11104
  namespace: string().optional(),
11042
11105
  collection: string(),
11043
11106
  field: string(),
@@ -11047,15 +11110,18 @@ method(_void(), EngineInfoSchema), method(object({
11047
11110
  }), array(object({
11048
11111
  bucket: number().int(),
11049
11112
  count: number().int()
11050
- })).readonly()), method(object({
11113
+ })).readonly(), { auth: "admin" }), method(object({
11051
11114
  namespace: string().optional(),
11052
11115
  collection: string()
11053
- }), boolean()), method(object({
11116
+ }), boolean(), { auth: "admin" }), method(object({
11054
11117
  namespace: string().optional(),
11055
11118
  collection: string(),
11056
11119
  columns: array(CollectionColumnSchema).readonly(),
11057
11120
  indexes: array(CollectionIndexSchema).readonly().optional()
11058
- }), _void(), { kind: "mutation" });
11121
+ }), _void(), {
11122
+ kind: "mutation",
11123
+ auth: "admin"
11124
+ });
11059
11125
  /**
11060
11126
  * shm ring usage stats for a `frameSink: 'shm'` decoder session —
11061
11127
  * exposed via `decoder.getShmStats` so downstream consumers can
@@ -11721,6 +11787,27 @@ var LinkedDeviceSchema = object({
11721
11787
  features: array(string()),
11722
11788
  producesTrackedEvents: boolean().optional()
11723
11789
  });
11790
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
11791
+ * The batch answer needs the tag; the single-device answer already has it
11792
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
11793
+ var LinkedDevicesForDeviceSchema = object({
11794
+ deviceId: number(),
11795
+ mode: LinkedDevicesModeSchema,
11796
+ devices: array(LinkedDeviceSchema)
11797
+ });
11798
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
11799
+ * `getAllBindings` all answer in. Declared once: three copies of the same
11800
+ * object literal is exactly how the three drift apart. */
11801
+ var DeviceBindingsForDeviceSchema = object({
11802
+ deviceId: number(),
11803
+ entries: array(object({
11804
+ capName: string(),
11805
+ kind: _enum(["native", "wrapped"]),
11806
+ providerAddonId: string(),
11807
+ providerNodeId: string(),
11808
+ nativeAddonId: string()
11809
+ }))
11810
+ });
11724
11811
  var SavedDeviceRowSchema = object({
11725
11812
  /** Numeric id reserved at allocateDeviceId time. */
11726
11813
  id: number(),
@@ -11946,11 +12033,25 @@ method(object({
11946
12033
  projection: _enum(["full", "slim"]).optional(),
11947
12034
  /** Return only camera devices. Filtering server-side instead of
11948
12035
  * shipping 293 rows to find 12. */
11949
- isCamera: boolean().optional()
12036
+ isCamera: boolean().optional(),
12037
+ /**
12038
+ * Return only these device ids. For the caller that already KNOWS the
12039
+ * handful it wants and needs a field the id-bearing answer does not
12040
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12041
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12042
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12043
+ * refetches on the reconcile interval, on a phone.
12044
+ *
12045
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12046
+ * keys rather than rejecting them (verified against the live hub
12047
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12048
+ * it answers today and the caller filters as it already does.
12049
+ */
12050
+ deviceIds: array(number()).optional()
11950
12051
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
11951
12052
  mode: LinkedDevicesModeSchema,
11952
12053
  devices: array(LinkedDeviceSchema)
11953
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12054
+ })), 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({
11954
12055
  deviceId: number(),
11955
12056
  values: record(string(), unknown())
11956
12057
  }), object({ success: literal(true) }), {
@@ -11977,25 +12078,7 @@ method(object({
11977
12078
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
11978
12079
  kind: "mutation",
11979
12080
  auth: "admin"
11980
- }), method(object({ deviceId: number() }), object({
11981
- deviceId: number(),
11982
- entries: array(object({
11983
- capName: string(),
11984
- kind: _enum(["native", "wrapped"]),
11985
- providerAddonId: string(),
11986
- providerNodeId: string(),
11987
- nativeAddonId: string()
11988
- }))
11989
- })), method(object({}), array(object({
11990
- deviceId: number(),
11991
- entries: array(object({
11992
- capName: string(),
11993
- kind: _enum(["native", "wrapped"]),
11994
- providerAddonId: string(),
11995
- providerNodeId: string(),
11996
- nativeAddonId: string()
11997
- }))
11998
- }))), method(object({
12081
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
11999
12082
  deviceId: number(),
12000
12083
  capName: string(),
12001
12084
  wrapperAddonId: string(),
@@ -12166,7 +12249,7 @@ method(object({
12166
12249
  crop: _instanceof(Uint8Array),
12167
12250
  width: number(),
12168
12251
  height: number()
12169
- }), EmbeddingResultSchema), method(object({ text: string() }), EmbeddingResultSchema), method(_void(), EmbeddingInfoSchema);
12252
+ }), EmbeddingResultSchema, { auth: "admin" }), method(object({ text: string() }), EmbeddingResultSchema, { auth: "admin" }), method(_void(), EmbeddingInfoSchema, { auth: "admin" });
12170
12253
  /**
12171
12254
  * filesystem-browse — per-node capability for browsing the node's local
12172
12255
  * filesystem. Reads are unconfined (whole filesystem, from `/` down); WRITES
@@ -12459,19 +12542,22 @@ method(LlmGenerateBaseInputSchema.extend({
12459
12542
  runtime: ManagedRuntimeConfigSchema,
12460
12543
  /** The managed profile's timeout, threaded by the hub provider. */
12461
12544
  timeoutMs: number().int().positive().optional()
12462
- }), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
12545
+ }), LlmGenerateResultSchema, {
12546
+ kind: "mutation",
12547
+ auth: "admin"
12548
+ }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
12463
12549
  kind: "mutation",
12464
12550
  auth: "admin"
12465
12551
  }), method(object({}), _void(), {
12466
12552
  kind: "mutation",
12467
12553
  auth: "admin"
12468
- }), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
12554
+ }), method(object({}), LlmRuntimeStatusSchema, { auth: "admin" }), method(object({ model: ManagedModelRefSchema }), _void(), {
12469
12555
  kind: "mutation",
12470
12556
  auth: "admin"
12471
12557
  }), method(object({ file: string() }), _void(), {
12472
12558
  kind: "mutation",
12473
12559
  auth: "admin"
12474
- }), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
12560
+ }), method(object({}), array(LlmNodeModelSchema), { auth: "admin" }), method(object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" });
12475
12561
  /**
12476
12562
  * `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
12477
12563
  * methods concat-fan across providers; single-row methods route to ONE
@@ -14367,12 +14453,15 @@ var NcOccupancyConditionSchema = object({
14367
14453
  * there is no second switch that can disagree with the first and every rule
14368
14454
  * authored before the decision migrates for free (`audioModeOf`):
14369
14455
  *
14370
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14371
- * classifier labels with one of them. No window, no percentage:
14372
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14373
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14374
- * the analyzer's (`classificationMinScore`, per device) a label only
14375
- * reaches this condition if the classifier was already confident enough.
14456
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14457
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14458
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
14459
+ * frames is the wrong question for a classifier that labels 1–3 frames
14460
+ * per episode. The count window is the brake that drops a single-frame
14461
+ * false positive; the rule's own `throttle` cooldown is the other. The
14462
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14463
+ * per device) — a label only reaches this condition if the classifier was
14464
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14376
14465
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14377
14466
  * the condition: at least `hitPercent`% of the samples over
14378
14467
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14399,14 +14488,22 @@ var NcOccupancyConditionSchema = object({
14399
14488
  * an operator who typed `dog` mean the same thing.
14400
14489
  */
14401
14490
  var NcAudioConditionSchema = object({
14402
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14491
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14403
14492
  labels: array(string().min(1)).min(1).optional(),
14404
14493
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14405
14494
  dbThreshold: number().min(-96).max(0).optional(),
14406
14495
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14407
14496
  hitPercent: number().int().min(1).max(100).default(60),
14408
14497
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14409
- samplingSeconds: number().int().min(1).max(300).default(10)
14498
+ samplingSeconds: number().int().min(1).max(300).default(10),
14499
+ /**
14500
+ * LABEL MODE: how many labelled frames must land inside
14501
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
14502
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
14503
+ */
14504
+ confirmHits: number().int().min(1).max(20).optional(),
14505
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
14506
+ confirmWindowSec: number().int().min(1).max(60).optional()
14410
14507
  });
14411
14508
  /**
14412
14509
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -15933,7 +16030,7 @@ var OauthIntegrationDescriptorSchema = object({
15933
16030
  */
15934
16031
  refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
15935
16032
  });
15936
- method(_void(), OauthIntegrationDescriptorSchema);
16033
+ method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
15937
16034
  /**
15938
16035
  * pipeline-analytics — device-scoped wrapper cap. Refines raw
15939
16036
  * per-frame detections emitted by the pipeline runner into tracked
@@ -16780,6 +16877,46 @@ var RecentTracksPageSchema = object({
16780
16877
  /** Cursor for the next page, or null when this page is the last. */
16781
16878
  nextCursor: string().nullable()
16782
16879
  });
16880
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
16881
+ var LIST_GROUPS_MAX_LIMIT = 100;
16882
+ var AnalyticsGroupRecordSchema = object({
16883
+ id: string(),
16884
+ deviceId: number().int(),
16885
+ openedAt: number().int(),
16886
+ closedAt: number().int(),
16887
+ timestamp: number().int(),
16888
+ memberCount: number().int(),
16889
+ memberTrackIds: array(string()).readonly(),
16890
+ className: string(),
16891
+ classes: array(string()).readonly(),
16892
+ /** Relative event-media path, or null when the group has no picture yet. */
16893
+ mediaUrl: string().nullable(),
16894
+ singleton: boolean()
16895
+ });
16896
+ var AnalyticsGroupMemberSchema = object({
16897
+ trackId: string(),
16898
+ deviceId: number().int(),
16899
+ className: string(),
16900
+ firstSeen: number().int(),
16901
+ lastSeen: number().int(),
16902
+ mediaUrl: string().nullable()
16903
+ });
16904
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
16905
+ var ListGroupsQueryInput = object({
16906
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
16907
+ deviceIds: array(number()),
16908
+ /** Window lower bound on `closedAt` (inclusive). */
16909
+ since: number().optional(),
16910
+ /** Window upper bound on `openedAt` (inclusive). */
16911
+ until: number().optional(),
16912
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
16913
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
16914
+ cursor: string().optional()
16915
+ });
16916
+ var ListGroupsPageSchema = object({
16917
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
16918
+ nextCursor: string().nullable()
16919
+ });
16783
16920
  var KeyEventQueryInput = object({
16784
16921
  deviceId: number(),
16785
16922
  /** Window lower bound (track firstSeen ≥ since). */
@@ -16855,7 +16992,9 @@ var TrackCascadeCountsSchema = object({
16855
16992
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
16856
16993
  plates: number().int(),
16857
16994
  /** Per-track CLIP search vectors removed (best-effort). */
16858
- embeddings: number().int()
16995
+ embeddings: number().int(),
16996
+ /** Group membership + group rows removed with their last member (best-effort). */
16997
+ groups: number().int()
16859
16998
  });
16860
16999
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
16861
17000
  var DiskReconcileCountsSchema = object({
@@ -17001,7 +17140,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17001
17140
  * stationary registry). Default false: the timeline lists passages,
17002
17141
  * not parking records (operator decision, 2026-08-15). */
17003
17142
  includeStationary: boolean().optional()
17004
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17143
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17144
+ deviceId: number(),
17145
+ groupId: string().min(1)
17146
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17005
17147
  kind: "mutation",
17006
17148
  auth: "admin"
17007
17149
  }), 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({
@@ -17083,6 +17225,15 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17083
17225
  }), method(object({ jobId: string() }), RelocateJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
17084
17226
  kind: "mutation",
17085
17227
  auth: "admin"
17228
+ }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
17229
+ kind: "mutation",
17230
+ auth: "admin"
17231
+ }), method(object({}), array(RelocateJobSchema).readonly(), {
17232
+ kind: "query",
17233
+ auth: "admin"
17234
+ }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
17235
+ kind: "mutation",
17236
+ auth: "admin"
17086
17237
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
17087
17238
  kind: "query",
17088
17239
  auth: "admin"
@@ -17237,6 +17388,11 @@ object({
17237
17388
  "jpeg"
17238
17389
  ])
17239
17390
  });
17391
+ /**
17392
+ * Process-local frame identity. It is serializable so it can ride an in-process
17393
+ * capability call, but `registryId` deliberately prevents resolution in any
17394
+ * other process or execution group.
17395
+ */
17240
17396
  var FrameRefSchema = object({
17241
17397
  registryId: string().min(1),
17242
17398
  id: string().min(1),
@@ -17311,7 +17467,8 @@ var PipelineModelOptionSchema = object({
17311
17467
  sizeMB: number()
17312
17468
  })),
17313
17469
  group: ModelVariantGroupSchema.optional(),
17314
- legacy: boolean().optional()
17470
+ legacy: boolean().optional(),
17471
+ provider: ModelProviderIdSchema.optional()
17315
17472
  });
17316
17473
  var ConfigFieldBridge = custom();
17317
17474
  var PipelineAddonSchemaSchema = object({
@@ -19401,7 +19558,7 @@ method(object({
19401
19558
  * linking rather than produce an eternal token.
19402
19559
  */
19403
19560
  ttlSec: union([number().int().positive(), literal("never")]).optional()
19404
- }), object({ token: string() })), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable());
19561
+ }), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
19405
19562
  var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
19406
19563
  providerId: string().min(1),
19407
19564
  displayName: string().min(1),
@@ -19496,10 +19653,13 @@ var EvictResultSchema = object({
19496
19653
  /** True when the provider has nothing left it is willing to drop on this location. */
19497
19654
  exhausted: boolean()
19498
19655
  });
19499
- method(object({ locationId: string() }), EvictableUsageSchema), method(object({
19656
+ method(object({ locationId: string() }), EvictableUsageSchema, { auth: "admin" }), method(object({
19500
19657
  locationId: string(),
19501
19658
  targetBytes: number().int().positive()
19502
- }), EvictResultSchema, { kind: "mutation" });
19659
+ }), EvictResultSchema, {
19660
+ kind: "mutation",
19661
+ auth: "admin"
19662
+ });
19503
19663
  method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin" }), method(StorageMigrationInputSchema, object({ jobId: string() }), {
19504
19664
  kind: "mutation",
19505
19665
  auth: "admin"
@@ -19585,43 +19745,82 @@ var storageProviderCapability = {
19585
19745
  mode: "collection",
19586
19746
  internal: true,
19587
19747
  methods: {
19748
+ /**
19749
+ * Every method below except `testLocation` had NO `auth` override —
19750
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
19751
+ * `data-store-provider`), so this was reachable on the AppRouter by ANY
19752
+ * authenticated session at the default `auth: 'protected'`: arbitrary
19753
+ * read/write/delete/list against any storage location the operator has
19754
+ * configured, keyed only by a caller-supplied `location` + `relativePath`
19755
+ * — the same "public consumers never see this cap" claim that was false
19756
+ * for `data-store-provider`, here gating raw file I/O instead of raw
19757
+ * settings rows. The only legitimate caller,
19758
+ * `addon-registry.service.ts`'s `activeStorageProvider`, calls the
19759
+ * provider object directly (in-process) — never tRPC — so `auth: 'admin'`
19760
+ * on every method costs it nothing. Public consumers already go through
19761
+ * the separate `storage` singleton cap.
19762
+ */
19588
19763
  /** Self-description for the "Add location" wizard. */
19589
- getProviderInfo: method(_void(), ProviderInfoSchema),
19764
+ getProviderInfo: method(_void(), ProviderInfoSchema, { auth: "admin" }),
19590
19765
  /** Validate config + probe connectivity. Called on add and on edit. */
19591
19766
  testLocation: method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }),
19592
19767
  resolve: method(object({
19593
19768
  location: StorageLocationSchema,
19594
19769
  relativePath: string()
19595
- }), string()),
19770
+ }), string(), { auth: "admin" }),
19596
19771
  write: method(object({
19597
19772
  location: StorageLocationSchema,
19598
19773
  relativePath: string(),
19599
19774
  data: _instanceof(Uint8Array)
19600
- }), _void(), { kind: "mutation" }),
19775
+ }), _void(), {
19776
+ kind: "mutation",
19777
+ auth: "admin"
19778
+ }),
19601
19779
  read: method(object({
19602
19780
  location: StorageLocationSchema,
19603
19781
  relativePath: string()
19604
- }), _instanceof(Uint8Array)),
19782
+ }), _instanceof(Uint8Array), { auth: "admin" }),
19605
19783
  exists: method(object({
19606
19784
  location: StorageLocationSchema,
19607
19785
  relativePath: string()
19608
- }), boolean()),
19786
+ }), boolean(), { auth: "admin" }),
19609
19787
  list: method(object({
19610
19788
  location: StorageLocationSchema,
19611
19789
  prefix: string().optional()
19612
- }), array(string()).readonly()),
19790
+ }), array(string()).readonly(), { auth: "admin" }),
19613
19791
  delete: method(object({
19614
19792
  location: StorageLocationSchema,
19615
19793
  relativePath: string()
19616
- }), _void(), { kind: "mutation" }),
19617
- getAvailableSpace: method(object({ location: StorageLocationSchema }), number().nullable()),
19618
- beginUpload: method(BeginUploadInputSchema, BeginUploadResultSchema, { kind: "mutation" }),
19619
- writeChunk: method(WriteChunkInputSchema, _void(), { kind: "mutation" }),
19620
- finalizeUpload: method(FinalizeUploadInputSchema, _void(), { kind: "mutation" }),
19621
- abortUpload: method(AbortUploadInputSchema, _void(), { kind: "mutation" }),
19622
- beginDownload: method(BeginDownloadInputSchema, BeginDownloadResultSchema, { kind: "mutation" }),
19623
- readChunk: method(ReadChunkInputSchema, _instanceof(Uint8Array)),
19624
- endDownload: method(EndDownloadInputSchema, _void(), { kind: "mutation" })
19794
+ }), _void(), {
19795
+ kind: "mutation",
19796
+ auth: "admin"
19797
+ }),
19798
+ getAvailableSpace: method(object({ location: StorageLocationSchema }), number().nullable(), { auth: "admin" }),
19799
+ beginUpload: method(BeginUploadInputSchema, BeginUploadResultSchema, {
19800
+ kind: "mutation",
19801
+ auth: "admin"
19802
+ }),
19803
+ writeChunk: method(WriteChunkInputSchema, _void(), {
19804
+ kind: "mutation",
19805
+ auth: "admin"
19806
+ }),
19807
+ finalizeUpload: method(FinalizeUploadInputSchema, _void(), {
19808
+ kind: "mutation",
19809
+ auth: "admin"
19810
+ }),
19811
+ abortUpload: method(AbortUploadInputSchema, _void(), {
19812
+ kind: "mutation",
19813
+ auth: "admin"
19814
+ }),
19815
+ beginDownload: method(BeginDownloadInputSchema, BeginDownloadResultSchema, {
19816
+ kind: "mutation",
19817
+ auth: "admin"
19818
+ }),
19819
+ readChunk: method(ReadChunkInputSchema, _instanceof(Uint8Array), { auth: "admin" }),
19820
+ endDownload: method(EndDownloadInputSchema, _void(), {
19821
+ kind: "mutation",
19822
+ auth: "admin"
19823
+ })
19625
19824
  }
19626
19825
  };
19627
19826
  /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
@@ -19881,7 +20080,8 @@ method(object({
19881
20080
  access: "create"
19882
20081
  }), method(object({ userId: string().optional() }), object({ optionsJSON: record(string(), unknown()) }), {
19883
20082
  kind: "mutation",
19884
- access: "view"
20083
+ access: "view",
20084
+ auth: "admin"
19885
20085
  }), method(object({
19886
20086
  /** Required — the user the assertion belongs to (verified). */
19887
20087
  userId: string(),
@@ -19889,10 +20089,12 @@ method(object({
19889
20089
  response: record(string(), unknown())
19890
20090
  }), object({ verified: boolean() }), {
19891
20091
  kind: "mutation",
19892
- access: "view"
20092
+ access: "view",
20093
+ auth: "admin"
19893
20094
  }), method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
19894
20095
  kind: "mutation",
19895
- access: "view"
20096
+ access: "view",
20097
+ auth: "admin"
19896
20098
  }), method(object({
19897
20099
  /** AuthenticationResponseJSON from the browser. */
19898
20100
  response: record(string(), unknown()) }), object({
@@ -19900,7 +20102,8 @@ response: record(string(), unknown()) }), object({
19900
20102
  userId: string().nullable()
19901
20103
  }), {
19902
20104
  kind: "mutation",
19903
- access: "view"
20105
+ access: "view",
20106
+ auth: "admin"
19904
20107
  }), method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }), method(object({
19905
20108
  userId: string(),
19906
20109
  credentialId: string()
@@ -20072,7 +20275,19 @@ var VectorStatsResultSchema = object({
20072
20275
  /** False when the backend ranks approximately. */
20073
20276
  exact: boolean()
20074
20277
  });
20075
- 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);
20278
+ method(VectorDeclareIndexInputSchema, _void(), {
20279
+ kind: "mutation",
20280
+ auth: "admin"
20281
+ }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
20282
+ kind: "mutation",
20283
+ auth: "admin"
20284
+ }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
20285
+ kind: "mutation",
20286
+ auth: "admin"
20287
+ }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
20288
+ kind: "mutation",
20289
+ auth: "admin"
20290
+ }), method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" });
20076
20291
  var ClipSchema = object({
20077
20292
  /** Opaque, provider-namespaced id. The default provider encodes the time
20078
20293
  * window so `getClipPlayback` is self-contained (no event re-query). */
@@ -21795,7 +22010,27 @@ var MediaFileLiteSchema$1 = object({
21795
22010
  sizeBytes: number(),
21796
22011
  timestamp: number()
21797
22012
  });
21798
- method(_void(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
22013
+ method(object({
22014
+ /**
22015
+ * Inline {@link IdentitySchema.coverBase64} on every row.
22016
+ *
22017
+ * Default `false`, the same inversion `listRecentFaces` and
22018
+ * `listPlates` took on 2026-08-25 (see `include-crops-default.ts` for
22019
+ * why the burden belongs on the caller that WANTS the bytes). Measured
22020
+ * on the live hub the same day: four identities cost 40,979 B with the
22021
+ * covers inline, ~10 KB of base64 per row, on a query this UI mounts
22022
+ * four times and the viewer holds at `staleTime: 30_000`.
22023
+ *
22024
+ * Nothing loses its avatar: `coverMediaKey` is already on every row and
22025
+ * the `event-media` plane serves that key `immutable` with an ETag.
22026
+ *
22027
+ * **This is an INPUT field, so it does not reach the addon until the
22028
+ * next train** — the hub router validates cap inputs against its own
22029
+ * compiled Zod and strips a key it does not know. Until then the
22030
+ * provider sees `undefined`, which resolves to `false`: the cheap shape
22031
+ * is what ships, and the opt-in becomes reachable when the train lands.
22032
+ */
22033
+ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
21799
22034
  kind: "mutation",
21800
22035
  auth: "admin"
21801
22036
  }), method(object({
@@ -23940,7 +24175,14 @@ var PlateInfoSchema = object({
23940
24175
  plateBbox: BoundingBoxSchema.optional(),
23941
24176
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
23942
24177
  keyFrameMediaKey: string().optional(),
23943
- base64: string().optional()
24178
+ base64: string().optional(),
24179
+ /**
24180
+ * Same crop as a data-plane URL, always present when the plate has a stored
24181
+ * crop. `getPlateByTrack` returns this and never inlines JPEG; `listPlates`
24182
+ * and `searchPlates` inline the crop only when their `includeCrops` input is
24183
+ * left at its `true` default.
24184
+ */
24185
+ cropUrl: string().optional()
23944
24186
  });
23945
24187
  var MediaFileLiteSchema = object({
23946
24188
  key: string(),
@@ -23958,14 +24200,34 @@ var PlateClusterSchema = object({
23958
24200
  });
23959
24201
  method(object({
23960
24202
  deviceId: number().int().optional(),
23961
- limit: number().int().positive().optional()
24203
+ limit: number().int().positive().optional(),
24204
+ /**
24205
+ * Inline the base64 crop on every row. Default `true` — the existing
24206
+ * behaviour, kept so no caller breaks.
24207
+ *
24208
+ * Set `false` once the caller renders {@link PlateInfo.cropUrl}.
24209
+ * Measured on the live hub at the 500 rows the Plates view asks for:
24210
+ * 879,403 B and 27.7 s with the crops inline, against a few KiB of
24211
+ * metadata without them — and the browser then caches the images.
24212
+ *
24213
+ * This is the plate twin of `faceGallery.listRecentFaces`'s option;
24214
+ * plates were the one gallery list left without it.
24215
+ *
24216
+ * **This is an INPUT field, so it does not reach the addon until the
24217
+ * next train.** The hub router validates cap inputs against its own
24218
+ * compiled Zod and strips a key it does not know. Until the train
24219
+ * ships, sending `false` is harmless and keeps the crops inline.
24220
+ */
24221
+ includeCrops: boolean().optional()
23962
24222
  }).optional(), array(PlateInfoSchema).readonly()), method(object({
23963
24223
  deviceId: number().int(),
23964
24224
  trackId: string()
23965
24225
  }), PlateInfoSchema.nullable()), method(object({ plateId: string() }), array(MediaFileLiteSchema).readonly()), method(object({
23966
24226
  text: string().min(1),
23967
24227
  maxDistance: number().int().min(0).optional(),
23968
- limit: number().int().positive().optional()
24228
+ limit: number().int().positive().optional(),
24229
+ /** See `listPlates.includeCrops`. Default `true`. */
24230
+ includeCrops: boolean().optional()
23969
24231
  }), array(PlateInfoSchema).readonly()), method(object({
23970
24232
  maxDistance: number().int().min(0).optional(),
23971
24233
  minClusterSize: number().int().min(2).optional(),
@@ -23979,7 +24241,13 @@ method(object({
23979
24241
  }), method(object({ plateId: string() }), _void(), {
23980
24242
  kind: "mutation",
23981
24243
  auth: "admin"
23982
- }), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
24244
+ }), method(object({
24245
+ /** Inline {@link VehicleSchema.coverBase64} on every row. Default
24246
+ * `false` — the vehicle twin of `faceGallery.listIdentities`'s
24247
+ * option; `coverMediaKey` + the `event-media` plane carry the picture.
24248
+ * INPUT field: stripped by the hub router until the train ships, which
24249
+ * resolves to `false` and is exactly the intended default. */
24250
+ includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
23983
24251
  kind: "mutation",
23984
24252
  auth: "admin"
23985
24253
  }), method(object({
@@ -27580,6 +27848,12 @@ Object.freeze({
27580
27848
  addonId: null,
27581
27849
  access: "view"
27582
27850
  },
27851
+ "deviceManager.getBindingsBatch": {
27852
+ capName: "device-manager",
27853
+ capScope: "system",
27854
+ addonId: null,
27855
+ access: "view"
27856
+ },
27583
27857
  "deviceManager.getChildren": {
27584
27858
  capName: "device-manager",
27585
27859
  capScope: "system",
@@ -27640,6 +27914,12 @@ Object.freeze({
27640
27914
  addonId: null,
27641
27915
  access: "view"
27642
27916
  },
27917
+ "deviceManager.getLinkedDevicesBatch": {
27918
+ capName: "device-manager",
27919
+ capScope: "system",
27920
+ addonId: null,
27921
+ access: "view"
27922
+ },
27643
27923
  "deviceManager.getRoleDisplayDefaults": {
27644
27924
  capName: "device-manager",
27645
27925
  capScope: "system",
@@ -29344,6 +29624,12 @@ Object.freeze({
29344
29624
  addonId: null,
29345
29625
  access: "create"
29346
29626
  },
29627
+ "pipelineAnalytics.cancelRelocateMedia": {
29628
+ capName: "pipeline-analytics",
29629
+ capScope: "device",
29630
+ addonId: null,
29631
+ access: "create"
29632
+ },
29347
29633
  "pipelineAnalytics.cancelStorageMigrationMove": {
29348
29634
  capName: "pipeline-analytics",
29349
29635
  capScope: "device",
@@ -29410,6 +29696,12 @@ Object.freeze({
29410
29696
  addonId: null,
29411
29697
  access: "view"
29412
29698
  },
29699
+ "pipelineAnalytics.getGroup": {
29700
+ capName: "pipeline-analytics",
29701
+ capScope: "device",
29702
+ addonId: null,
29703
+ access: "view"
29704
+ },
29413
29705
  "pipelineAnalytics.getKeyEvents": {
29414
29706
  capName: "pipeline-analytics",
29415
29707
  capScope: "device",
@@ -29494,6 +29786,12 @@ Object.freeze({
29494
29786
  addonId: null,
29495
29787
  access: "view"
29496
29788
  },
29789
+ "pipelineAnalytics.listGroups": {
29790
+ capName: "pipeline-analytics",
29791
+ capScope: "device",
29792
+ addonId: null,
29793
+ access: "view"
29794
+ },
29497
29795
  "pipelineAnalytics.listOpsLog": {
29498
29796
  capName: "pipeline-analytics",
29499
29797
  capScope: "device",
@@ -29506,6 +29804,12 @@ Object.freeze({
29506
29804
  addonId: null,
29507
29805
  access: "view"
29508
29806
  },
29807
+ "pipelineAnalytics.listRelocateMediaJobs": {
29808
+ capName: "pipeline-analytics",
29809
+ capScope: "device",
29810
+ addonId: null,
29811
+ access: "view"
29812
+ },
29509
29813
  "pipelineAnalytics.listRetrainAnnotations": {
29510
29814
  capName: "pipeline-analytics",
29511
29815
  capScope: "device",
@@ -29584,6 +29888,12 @@ Object.freeze({
29584
29888
  addonId: null,
29585
29889
  access: "create"
29586
29890
  },
29891
+ "pipelineAnalytics.relocateMedia": {
29892
+ capName: "pipeline-analytics",
29893
+ capScope: "device",
29894
+ addonId: null,
29895
+ access: "create"
29896
+ },
29587
29897
  "pipelineAnalytics.restageRetrainTrack": {
29588
29898
  capName: "pipeline-analytics",
29589
29899
  capScope: "device",
@@ -32275,6 +32585,11 @@ Object.freeze({
32275
32585
  form: "single",
32276
32586
  optional: false
32277
32587
  }],
32588
+ "deviceManager.getBindingsBatch": [{
32589
+ name: "deviceIds",
32590
+ form: "array",
32591
+ optional: false
32592
+ }],
32278
32593
  "deviceManager.getChildren": [{
32279
32594
  name: "parentDeviceId",
32280
32595
  form: "single",
@@ -32320,6 +32635,11 @@ Object.freeze({
32320
32635
  form: "single",
32321
32636
  optional: false
32322
32637
  }],
32638
+ "deviceManager.getLinkedDevicesBatch": [{
32639
+ name: "deviceIds",
32640
+ form: "array",
32641
+ optional: false
32642
+ }],
32323
32643
  "deviceManager.getSettingsSchema": [{
32324
32644
  name: "deviceId",
32325
32645
  form: "single",
@@ -32340,6 +32660,11 @@ Object.freeze({
32340
32660
  form: "single",
32341
32661
  optional: false
32342
32662
  }],
32663
+ "deviceManager.listAll": [{
32664
+ name: "deviceIds",
32665
+ form: "array",
32666
+ optional: true
32667
+ }],
32343
32668
  "deviceManager.loadConfig": [{
32344
32669
  name: "deviceId",
32345
32670
  form: "single",
@@ -32913,6 +33238,11 @@ Object.freeze({
32913
33238
  form: "single",
32914
33239
  optional: false
32915
33240
  }],
33241
+ "pipelineAnalytics.getGroup": [{
33242
+ name: "deviceId",
33243
+ form: "single",
33244
+ optional: false
33245
+ }],
32916
33246
  "pipelineAnalytics.getKeyEvents": [{
32917
33247
  name: "deviceId",
32918
33248
  form: "single",
@@ -32968,6 +33298,11 @@ Object.freeze({
32968
33298
  form: "array",
32969
33299
  optional: false
32970
33300
  }],
33301
+ "pipelineAnalytics.listGroups": [{
33302
+ name: "deviceIds",
33303
+ form: "array",
33304
+ optional: false
33305
+ }],
32971
33306
  "pipelineAnalytics.listOpsLog": [{
32972
33307
  name: "deviceId",
32973
33308
  form: "single",