@camstack/addon-osd-manager 0.1.21 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (18) hide show
  1. package/dist/{MotionZonesSettings-C1tuU9vA.mjs → MotionZonesSettings-DhYwaRz3.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-BfYwmnx-.mjs → PrivacyMaskSettings-BquvldBM.mjs} +4 -4
  3. package/dist/{SceneMonitorEditor-Fui6aPeX.mjs → SceneMonitorEditor-wK0ulSnf.mjs} +3 -3
  4. package/dist/_stub.js +10 -10
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-CUYUIYix.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-CG-BhZBB.mjs} +4 -4
  6. package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-6_-m0FOQ.mjs +26 -0
  7. package/dist/{hostInit-wPjKQ8mT.mjs → hostInit-8p7aKRSc.mjs} +3 -3
  8. package/dist/index.js +465 -100
  9. package/dist/index.mjs +465 -100
  10. package/dist/{player-overlays-B_C4YyEi.mjs → player-overlays-CS4sZzYZ.mjs} +1 -1
  11. package/dist/remoteEntry.js +1 -1
  12. package/dist/{responsive-BzwsIYSh.mjs → responsive-VdLah82L.mjs} +1 -1
  13. package/dist/{square-X_zDrz0l.mjs → square-a4rMgWr9.mjs} +1 -1
  14. package/dist/{trash-2-BldQMEFN.mjs → trash-2-BCaHwGDI.mjs} +1 -1
  15. package/dist/{use-device-snapshot-Bxo1wH26.mjs → use-device-snapshot-CTWHDX3F.mjs} +1 -1
  16. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-DZH4YPzH.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-BdfKcHRf.mjs} +1 -1
  17. package/package.json +1 -1
  18. package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BhABwRxy.mjs +0 -26
package/dist/index.js CHANGED
@@ -6633,7 +6633,7 @@ function method(input, output, options) {
6633
6633
  input,
6634
6634
  output,
6635
6635
  kind: options?.kind ?? "query",
6636
- auth: options?.auth ?? "protected",
6636
+ ...options?.auth !== void 0 ? { auth: options.auth } : {},
6637
6637
  ...options?.access !== void 0 ? { access: options.access } : {},
6638
6638
  ...options?.caller !== void 0 ? { caller: options.caller } : {},
6639
6639
  timeoutMs: options?.timeoutMs
@@ -6663,8 +6663,17 @@ var adminUiCapability = {
6663
6663
  mode: "singleton",
6664
6664
  internal: true,
6665
6665
  methods: {
6666
- getStaticDir: method(_void(), StaticDirOutputSchema$1),
6667
- getVersion: method(_void(), VersionOutputSchema$1)
6666
+ /**
6667
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
6668
+ * `data-store-provider`) — both methods were reachable on the AppRouter
6669
+ * by ANY authenticated session at the default `auth: 'protected'`, and
6670
+ * `getStaticDir` leaks a hub filesystem path. The only caller is
6671
+ * `main.ts`'s static-file bootstrap, via `capRegistry.getSingletonForNode`
6672
+ * — never tRPC — so `auth: 'admin'` costs it nothing. The actual admin-ui
6673
+ * SPA assets are served as plain static files, unaffected by this gate.
6674
+ */
6675
+ getStaticDir: method(_void(), StaticDirOutputSchema$1, { auth: "admin" }),
6676
+ getVersion: method(_void(), VersionOutputSchema$1, { auth: "admin" })
6668
6677
  }
6669
6678
  };
6670
6679
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
@@ -7000,8 +7009,23 @@ var viewerUiCapability = {
7000
7009
  mode: "singleton",
7001
7010
  internal: true,
7002
7011
  methods: {
7003
- getStaticDir: method(_void(), StaticDirOutputSchema),
7004
- getVersion: method(_void(), VersionOutputSchema)
7012
+ /**
7013
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
7014
+ * `data-store-provider`) — both methods were reachable on the AppRouter
7015
+ * by ANY authenticated session at the default `auth: 'protected'`, and
7016
+ * `getStaticDir` leaks a hub filesystem path.
7017
+ *
7018
+ * ⚠ Checked before gating: the VIEWER app IS used by a non-admin user
7019
+ * (`roberta`), but it never calls `viewerUi.*` over tRPC. The static SPA
7020
+ * it loads is served by the plain `GET /viewer/*` Fastify route in
7021
+ * `main.ts`, which reads `staticDir`/`indexPath` from a module-level
7022
+ * cache populated ONCE at boot via `capRegistry.getSingletonForNode`
7023
+ * (in-process, not tRPC) — the cap's tRPC procedure is never on that
7024
+ * request path. `auth: 'admin'` here does not block a single viewer
7025
+ * user from loading the app.
7026
+ */
7027
+ getStaticDir: method(_void(), StaticDirOutputSchema, { auth: "admin" }),
7028
+ getVersion: method(_void(), VersionOutputSchema, { auth: "admin" })
7005
7029
  }
7006
7030
  };
7007
7031
  /**
@@ -7676,24 +7700,6 @@ var RecordingRetentionSchema = object({
7676
7700
  maxSizeGb: number().min(0).optional()
7677
7701
  });
7678
7702
  /**
7679
- * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7680
- * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7681
- * previews at. Five graduated steps; absent on a config = `standard` (the
7682
- * shipped default, matching `sheet-geometry`/`sheet-composer`).
7683
- *
7684
- * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7685
- * Each window's index sidecar carries its own tile dims, so a camera whose
7686
- * preset changed over time renders every historical window at the dims it was
7687
- * written with.
7688
- */
7689
- var ScrubThumbnailPresetSchema = _enum([
7690
- "minimal",
7691
- "low",
7692
- "standard",
7693
- "high",
7694
- "max"
7695
- ]);
7696
- /**
7697
7703
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7698
7704
  *
7699
7705
  * `bands` is the ONLY authored recording intent: what to record, when, and on
@@ -7701,7 +7707,11 @@ var ScrubThumbnailPresetSchema = _enum([
7701
7707
  * other field is a storage knob (profiles, segment length, retention, scrub).
7702
7708
  *
7703
7709
  * STRICT on purpose: the legacy authoring surface (`schedule`/`schedules`/
7704
- * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30.
7710
+ * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30,
7711
+ * and `scrubThumbnails` — a five-step fidelity knob for a sprite tier that was
7712
+ * deleted on 2026-07-24 and had ZERO consumers in the recorder — on 2026-08-25
7713
+ * (D62: a switch that writes a store nobody reads is worse than no switch).
7714
+ * Stored rows keep loading: the READ schema is `.strip()` (config-store.ts).
7705
7715
  * A stale caller must fail loudly — silently stripping its legacy intent would
7706
7716
  * persist a band-less config, i.e. silently stop recording the camera.
7707
7717
  */
@@ -7724,14 +7734,7 @@ var RecordingConfigSchema = object({
7724
7734
  * "off" is the absence of a covering band, never a band value.
7725
7735
  */
7726
7736
  bands: array(RecordingBandSchema).default([]),
7727
- retention: RecordingRetentionSchema.optional(),
7728
- /**
7729
- * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7730
- * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7731
- * windows only — existing sheets are immutable, and each window's index
7732
- * carries its own tile dims so mixed-preset history renders correctly.
7733
- */
7734
- scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7737
+ retention: RecordingRetentionSchema.optional()
7735
7738
  }).strict();
7736
7739
  /**
7737
7740
  * Entity-relocation job state (storage entity-routing spec, Phase 4).
@@ -7807,10 +7810,11 @@ var RelocateFootageInputSchema = object({
7807
7810
  * `RecordingConfig.enabled` or camera wrapper bindings. */
7808
7811
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
7809
7812
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
7810
- var StorageMigrationMediaMoveInputSchema = object({
7813
+ var RelocateMediaInputSchema = object({
7811
7814
  toLocationId: string(),
7812
7815
  throttleMbps: number().min(1).max(1e3).optional()
7813
- }).extend({ leaseId: string().min(1) });
7816
+ });
7817
+ var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
7814
7818
  /** The independently selectable logical storage classes. `recordings`
7815
7819
  * encompasses the high and mid segment profiles; `recordingsLow` is low
7816
7820
  * segments; `eventMedia` is post-analysis blobs. */
@@ -8105,7 +8109,26 @@ var LabelDefinitionSchema = object({
8105
8109
  description: string().optional(),
8106
8110
  icon: string().optional()
8107
8111
  });
8108
- var ClassMapDefinitionSchema = object({
8112
+ /**
8113
+ * Wire schema for a per-model CATALOG classMap override
8114
+ * (`ModelCatalogEntry.classMap` / `ModelConvertMetadata.classMap`) —
8115
+ * restricted to {@link CLASS_MAP_MACRO_TARGETS}, the only macros the
8116
+ * detection pipeline executor actually routes.
8117
+ *
8118
+ * This is deliberately a DIFFERENT, narrower shape than the general-purpose
8119
+ * `ClassMapDefinition` interface above (e.g. `IDetectionAddon.getClassMap()`
8120
+ * and the audio `YAMNET_TO_MACRO` catalog both use macro targets outside this
8121
+ * enum) — the two used to share the name `ClassMapDefinition`/
8122
+ * `ClassMapDefinitionSchema`, which made the schema-type-twin guard
8123
+ * (`scripts/check-schema-type-twins.ts`) flag them as a duplicated shape. They
8124
+ * are not: it is two different concepts colliding on a name. Keep this type
8125
+ * under its own name rather than reusing `ClassMapDefinition` — reusing it
8126
+ * would either narrow every `ClassMapDefinition` consumer to the four
8127
+ * detection macros (breaking `YAMNET_TO_MACRO`) or drop the validation this
8128
+ * schema exists for (see the "rejects a classMap whose target is not a
8129
+ * detection macro" test in `model-catalog-schema.test.ts`).
8130
+ */
8131
+ var DetectionCatalogClassMapSchema = object({
8109
8132
  mapping: record(string(), _enum([
8110
8133
  "person",
8111
8134
  "vehicle",
@@ -8310,7 +8333,7 @@ var ModelCatalogEntrySchema = object({
8310
8333
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8311
8334
  * labels already ARE the CamStack macros (Scrypted identity map).
8312
8335
  */
8313
- classMap: ClassMapDefinitionSchema.optional()
8336
+ classMap: DetectionCatalogClassMapSchema.optional()
8314
8337
  });
8315
8338
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8316
8339
  format: literal("openvino"),
@@ -8340,7 +8363,7 @@ var ModelConvertMetadataSchema = object({
8340
8363
  "segmentation"
8341
8364
  ]),
8342
8365
  faceAlignment: boolean().optional(),
8343
- classMap: ClassMapDefinitionSchema.optional()
8366
+ classMap: DetectionCatalogClassMapSchema.optional()
8344
8367
  });
8345
8368
  var ConvertResultSchema = object({
8346
8369
  entry: ModelCatalogEntrySchema,
@@ -9213,7 +9236,18 @@ var addonPagesSourceCapability = {
9213
9236
  scope: "system",
9214
9237
  mode: "collection",
9215
9238
  internal: true,
9216
- methods: { listPages: method(_void(), array(AddonPageDeclarationSchema).readonly()) }
9239
+ methods: {
9240
+ /**
9241
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
9242
+ * `data-store-provider`) — this was reachable on the AppRouter by ANY
9243
+ * authenticated session at the default `auth: 'protected'`. The only
9244
+ * consumer, the `addon-pages-aggregator` builtin, reads it via
9245
+ * `ctx.capabilities.getCollection('addon-pages-source')` — LOCAL-PROCESS
9246
+ * only, never tRPC (per `ctx.capabilities` seeing only the local
9247
+ * process). The public, enriched listing admin-ui actually consumes is
9248
+ * the separate `addon-pages` cap, unaffected by this change.
9249
+ */
9250
+ listPages: method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" }) }
9217
9251
  };
9218
9252
  var AddonHttpRouteSchema = object({
9219
9253
  method: _enum([
@@ -9510,7 +9544,20 @@ var addonWidgetsSourceCapability = {
9510
9544
  scope: "system",
9511
9545
  mode: "collection",
9512
9546
  internal: true,
9513
- methods: { listWidgets: method(_void(), array(WidgetMetadataSchema).readonly()) }
9547
+ methods: {
9548
+ /**
9549
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
9550
+ * `data-store-provider`) — this was reachable on the AppRouter by ANY
9551
+ * authenticated session at the default `auth: 'protected'`. The only
9552
+ * consumer, the `addon-widgets-aggregator` builtin, reads it via
9553
+ * `ctx.capabilities.getCollection('addon-widgets-source')` — LOCAL-PROCESS
9554
+ * only, never tRPC. The public, enriched listing admin-ui actually
9555
+ * consumes is the separate `addon-widgets` cap (`listWidgets` there is
9556
+ * gated separately, and a `preAuth: true` widget is surfaced through the
9557
+ * PUBLIC `auth.listLoginMethods` contribution channel instead) —
9558
+ * neither is affected by this change.
9559
+ */
9560
+ listWidgets: method(_void(), array(WidgetMetadataSchema).readonly(), { auth: "admin" }) }
9514
9561
  };
9515
9562
  /**
9516
9563
  * `addon-widgets` — system-scoped singleton aggregator cap. Public-facing
@@ -11674,7 +11721,17 @@ var customModelRegistryCapability = {
11674
11721
  scope: "system",
11675
11722
  mode: "collection",
11676
11723
  internal: true,
11677
- methods: { listModels: method(_void(), array(CustomModelDescriptorSchema).readonly()) }
11724
+ methods: {
11725
+ /**
11726
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
11727
+ * `data-store-provider`) — this was reachable on the AppRouter by ANY
11728
+ * authenticated session at the default `auth: 'protected'`. The only
11729
+ * consumer, the detection-pipeline's model resolution
11730
+ * (`addon-pipeline/src/detection-pipeline/provider.ts`), calls
11731
+ * `ctx.api.customModelRegistry.listModels.query()` — UDS/Moleculer,
11732
+ * never tRPC — so `auth: 'admin'` costs it nothing.
11733
+ */
11734
+ listModels: method(_void(), array(CustomModelDescriptorSchema).readonly(), { auth: "admin" }) }
11678
11735
  };
11679
11736
  /**
11680
11737
  * Query filter for settings-store collections.
@@ -11798,11 +11855,26 @@ var settingsStoreCapability = {
11798
11855
  key: string(),
11799
11856
  value: unknown()
11800
11857
  }), _void(), { kind: "mutation" }),
11801
- /** Get all entries matching an optional filter. */
11858
+ /**
11859
+ * Rows matching a filter.
11860
+ *
11861
+ * `columns` is a SQL-level projection: the engine SELECTs only those
11862
+ * columns (plus the primary key) instead of the whole row. It is the only
11863
+ * way to stop paying disk for a column that is discarded — a heavy JSON
11864
+ * column dominates the row, and dropping it in JS after the read pays every
11865
+ * byte first. Measured on the live `pipeline-analytics:tracks` table: a
11866
+ * 501-row page is 4.6 MB whole and 0.11 MB projected, same index, same
11867
+ * plan.
11868
+ *
11869
+ * The engine REFUSES a column it cannot serve rather than omitting it —
11870
+ * see `SettingsQueryInput.columns` for why a projection may not be
11871
+ * forgiving the way a predicate is.
11872
+ */
11802
11873
  query: method(object({
11803
11874
  namespace: string().optional(),
11804
11875
  collection: string(),
11805
- filter: QueryFilterSchema.optional()
11876
+ filter: QueryFilterSchema.optional(),
11877
+ columns: array(string()).readonly().optional()
11806
11878
  }), array(SettingsRecordSchema).readonly()),
11807
11879
  /** Insert a new record. */
11808
11880
  insert: method(object({
@@ -11928,6 +12000,23 @@ var EngineInfoSchema = object({
11928
12000
  * go through `settings-store`; they never see this cap, and an engine
11929
12001
  * never sees a caller.
11930
12002
  *
12003
+ * ⚠ **`internal: true` DOES NOT gate the mount, and until 2026-08-26 this
12004
+ * comment claimed otherwise.** `resolveCapMount` never reads the field, so this
12005
+ * cap was live on the AppRouter with every method at the default
12006
+ * `auth: 'protected'` — i.e. readable by ANY authenticated session, admin or
12007
+ * not. Verified against the live hub: `dataStoreProvider.query` returned raw
12008
+ * settings rows including a provider credential, bypassing the secret redaction
12009
+ * that `deviceManager.listAll` and friends go through.
12010
+ *
12011
+ * Every method is now `auth: 'admin'` EXPLICITLY, including the five reads —
12012
+ * they had no options object at all, which is exactly how they kept the
12013
+ * permissive default while the mutations looked deliberate. The explicit
12014
+ * annotation is the fix that works today; making `internal` mean something at
12015
+ * mount time is the systemic one, and it is not free: 22 caps declare it, only
12016
+ * 4 are `mount: 'skip'`, and forcing admin on all of them would change the auth
12017
+ * of surfaces the viewer and admin-ui call as non-admin users. That decision is
12018
+ * the operator's, not a side effect of this file.
12019
+ *
11931
12020
  * Design notes:
11932
12021
  * - **Stateless dispatch.** Every method carries its own
11933
12022
  * `namespace` + `collection`, so an engine keeps no per-caller state
@@ -11949,64 +12038,105 @@ var dataStoreProviderCapability = {
11949
12038
  internal: true,
11950
12039
  methods: {
11951
12040
  /** Self-description — how the orchestrator picks a registrant. */
11952
- getEngineInfo: method(_void(), EngineInfoSchema),
12041
+ getEngineInfo: method(_void(), EngineInfoSchema, { auth: "admin" }),
11953
12042
  /** Get a single value by key from a collection. */
11954
12043
  get: method(object({
11955
12044
  namespace: string().optional(),
11956
12045
  collection: string(),
11957
12046
  key: string()
11958
- }), unknown()),
12047
+ }), unknown(), { auth: "admin" }),
11959
12048
  /** Set a value by key in a collection (upsert). */
11960
12049
  set: method(object({
11961
12050
  namespace: string().optional(),
11962
12051
  collection: string(),
11963
12052
  key: string(),
11964
12053
  value: unknown()
11965
- }), _void(), { kind: "mutation" }),
12054
+ }), _void(), {
12055
+ kind: "mutation",
12056
+ auth: "admin"
12057
+ }),
11966
12058
  /** Get all entries matching an optional filter. */
11967
12059
  query: method(object({
11968
12060
  namespace: string().optional(),
11969
12061
  collection: string(),
11970
- filter: QueryFilterSchema.optional()
11971
- }), array(SettingsRecordSchema).readonly()),
12062
+ filter: QueryFilterSchema.optional(),
12063
+ /**
12064
+ * SQL-level column projection — MUST mirror `settings-store.query`.
12065
+ *
12066
+ * ⚠ An earlier version of this comment blamed Zod stripping, and that
12067
+ * was wrong — corrected 2026-08-26 after the hop map
12068
+ * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
12069
+ * There is **no Zod parse at all** between the door and the engine: the
12070
+ * dispatcher forwards the payload verbatim and UDS carries it whole. A
12071
+ * field declared here reaches `SqliteSettingsBackend` either way.
12072
+ *
12073
+ * What actually lost `columns` was the THIRD declaration of this shape:
12074
+ * `SettingsQueryInput` in `interfaces/storage.ts`, a hand-written TS
12075
+ * interface the engine destructures from. The field existed on both
12076
+ * schemas and the engine still never read it, because nothing checks a
12077
+ * registered provider against `InferProvider<cap>` —
12078
+ * `ProviderRegistration.provider` is typed `object`.
12079
+ *
12080
+ * It is declared here anyway, and must stay in step with
12081
+ * `settings-store.query`: a caller reading only the cap definitions has
12082
+ * to be able to see that this call carries a projection.
12083
+ * `data-door-schema-parity.spec.ts` keeps the two aligned.
12084
+ */
12085
+ columns: array(string()).readonly().optional()
12086
+ }), array(SettingsRecordSchema).readonly(), { auth: "admin" }),
11972
12087
  /** Insert a new record. */
11973
12088
  insert: method(object({
11974
12089
  namespace: string().optional(),
11975
12090
  collection: string(),
11976
12091
  record: SettingsRecordSchema
11977
- }), _void(), { kind: "mutation" }),
12092
+ }), _void(), {
12093
+ kind: "mutation",
12094
+ auth: "admin"
12095
+ }),
11978
12096
  /** Update an existing record by ID. */
11979
12097
  update: method(object({
11980
12098
  namespace: string().optional(),
11981
12099
  collection: string(),
11982
12100
  id: string(),
11983
12101
  data: record(string(), unknown())
11984
- }), _void(), { kind: "mutation" }),
12102
+ }), _void(), {
12103
+ kind: "mutation",
12104
+ auth: "admin"
12105
+ }),
11985
12106
  /** Delete a record by key/ID. */
11986
12107
  delete: method(object({
11987
12108
  namespace: string().optional(),
11988
12109
  collection: string(),
11989
12110
  key: string()
11990
- }), _void(), { kind: "mutation" }),
12111
+ }), _void(), {
12112
+ kind: "mutation",
12113
+ auth: "admin"
12114
+ }),
11991
12115
  /** Delete every record matching `filter`, in one statement. */
11992
12116
  deleteWhere: method(object({
11993
12117
  namespace: string().optional(),
11994
12118
  collection: string(),
11995
12119
  filter: MutationFilterSchema
11996
- }), object({ deleted: number().int() }), { kind: "mutation" }),
12120
+ }), object({ deleted: number().int() }), {
12121
+ kind: "mutation",
12122
+ auth: "admin"
12123
+ }),
11997
12124
  /** Apply `data` to every record matching `filter`, in one statement. */
11998
12125
  updateWhere: method(object({
11999
12126
  namespace: string().optional(),
12000
12127
  collection: string(),
12001
12128
  filter: MutationFilterSchema,
12002
12129
  data: record(string(), unknown())
12003
- }), object({ updated: number().int() }), { kind: "mutation" }),
12130
+ }), object({ updated: number().int() }), {
12131
+ kind: "mutation",
12132
+ auth: "admin"
12133
+ }),
12004
12134
  /** Count entries in a collection, optionally filtered. */
12005
12135
  count: method(object({
12006
12136
  namespace: string().optional(),
12007
12137
  collection: string(),
12008
12138
  filter: QueryFilterSchema.optional()
12009
- }), number()),
12139
+ }), number(), { auth: "admin" }),
12010
12140
  /** Grouped counts per ((field-origin)/bucketSize) bucket, filtered. */
12011
12141
  histogram: method(object({
12012
12142
  namespace: string().optional(),
@@ -12018,19 +12148,22 @@ var dataStoreProviderCapability = {
12018
12148
  }), array(object({
12019
12149
  bucket: number().int(),
12020
12150
  count: number().int()
12021
- })).readonly()),
12151
+ })).readonly(), { auth: "admin" }),
12022
12152
  /** Check if a collection is empty. */
12023
12153
  isEmpty: method(object({
12024
12154
  namespace: string().optional(),
12025
12155
  collection: string()
12026
- }), boolean()),
12156
+ }), boolean(), { auth: "admin" }),
12027
12157
  /** Declare a typed (SQL-backed) collection with columns + indexes. */
12028
12158
  declareCollection: method(object({
12029
12159
  namespace: string().optional(),
12030
12160
  collection: string(),
12031
12161
  columns: array(CollectionColumnSchema).readonly(),
12032
12162
  indexes: array(CollectionIndexSchema).readonly().optional()
12033
- }), _void(), { kind: "mutation" })
12163
+ }), _void(), {
12164
+ kind: "mutation",
12165
+ auth: "admin"
12166
+ })
12034
12167
  }
12035
12168
  };
12036
12169
  /**
@@ -13830,13 +13963,20 @@ var embeddingEncoderCapability = {
13830
13963
  mode: "collection",
13831
13964
  internal: true,
13832
13965
  methods: {
13966
+ /**
13967
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
13968
+ * `data-store-provider`) — every method below was reachable on the
13969
+ * AppRouter by ANY authenticated session at the default
13970
+ * `auth: 'protected'`. Consumers reach the encoder via `ctx.api` from
13971
+ * `addon-post-analysis`, never tRPC.
13972
+ */
13833
13973
  encode: method(object({
13834
13974
  crop: _instanceof(Uint8Array),
13835
13975
  width: number(),
13836
13976
  height: number()
13837
- }), EmbeddingResultSchema),
13838
- encodeText: method(object({ text: string() }), EmbeddingResultSchema),
13839
- getInfo: method(_void(), EmbeddingInfoSchema)
13977
+ }), EmbeddingResultSchema, { auth: "admin" }),
13978
+ encodeText: method(object({ text: string() }), EmbeddingResultSchema, { auth: "admin" }),
13979
+ getInfo: method(_void(), EmbeddingInfoSchema, { auth: "admin" })
13840
13980
  }
13841
13981
  };
13842
13982
  /**
@@ -14145,12 +14285,28 @@ var llmRuntimeCapability = {
14145
14285
  mode: "singleton",
14146
14286
  internal: true,
14147
14287
  methods: {
14288
+ /**
14289
+ * `complete`, `status`, `listLocalModels` and `getDiskUsage` had NO
14290
+ * `auth` override — `internal: true` did not gate the mount (see the
14291
+ * 2026-08-26 note on `data-store-provider`), so they were reachable on
14292
+ * the AppRouter by ANY authenticated session at the default
14293
+ * `auth: 'protected'`, inconsistent with the rest of this cap (already
14294
+ * `auth: 'admin'`) and with this file's own docblock: "the operator
14295
+ * reaches this only through the `llm` cap's methods." The sole caller,
14296
+ * `addon-ai`'s `runtime-client.ts`, reaches every method here via
14297
+ * `ctx.api` + `nodePin` — never tRPC — so `auth: 'admin'` costs it
14298
+ * nothing; the public `llm` cap (which end users and non-admin sessions
14299
+ * legitimately call for chat/generation) is untouched.
14300
+ */
14148
14301
  complete: method(LlmGenerateBaseInputSchema.extend({
14149
14302
  images: array(LlmImageSchema).optional(),
14150
14303
  runtime: ManagedRuntimeConfigSchema,
14151
14304
  /** The managed profile's timeout, threaded by the hub provider. */
14152
14305
  timeoutMs: number().int().positive().optional()
14153
- }), LlmGenerateResultSchema, { kind: "mutation" }),
14306
+ }), LlmGenerateResultSchema, {
14307
+ kind: "mutation",
14308
+ auth: "admin"
14309
+ }),
14154
14310
  ensureStarted: method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
14155
14311
  kind: "mutation",
14156
14312
  auth: "admin"
@@ -14159,7 +14315,7 @@ var llmRuntimeCapability = {
14159
14315
  kind: "mutation",
14160
14316
  auth: "admin"
14161
14317
  }),
14162
- status: method(object({}), LlmRuntimeStatusSchema),
14318
+ status: method(object({}), LlmRuntimeStatusSchema, { auth: "admin" }),
14163
14319
  installModel: method(object({ model: ManagedModelRefSchema }), _void(), {
14164
14320
  kind: "mutation",
14165
14321
  auth: "admin"
@@ -14168,8 +14324,8 @@ var llmRuntimeCapability = {
14168
14324
  kind: "mutation",
14169
14325
  auth: "admin"
14170
14326
  }),
14171
- listLocalModels: method(object({}), array(LlmNodeModelSchema)),
14172
- getDiskUsage: method(object({}), LlmRuntimeDiskUsageSchema)
14327
+ listLocalModels: method(object({}), array(LlmNodeModelSchema), { auth: "admin" }),
14328
+ getDiskUsage: method(object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" })
14173
14329
  }
14174
14330
  };
14175
14331
  /**
@@ -18435,7 +18591,17 @@ var oauthIntegrationCapability = {
18435
18591
  scope: "system",
18436
18592
  mode: "collection",
18437
18593
  internal: true,
18438
- methods: { getDescriptor: method(_void(), OauthIntegrationDescriptorSchema) }
18594
+ methods: {
18595
+ /**
18596
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
18597
+ * `data-store-provider`) — `getDescriptor` was reachable on the AppRouter
18598
+ * by ANY authenticated session at the default `auth: 'protected'`. The
18599
+ * real caller is `/api/oauth2/authorize` and `/api/oauth2/integrations`
18600
+ * (`oauth2-routes.ts`), which resolve the provider directly off the
18601
+ * capability registry — never through tRPC. `auth: 'admin'` closes the
18602
+ * tRPC surface without touching that path.
18603
+ */
18604
+ getDescriptor: method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" }) }
18439
18605
  };
18440
18606
  /**
18441
18607
  * pipeline-analytics — device-scoped wrapper cap. Refines raw
@@ -19834,6 +20000,29 @@ var pipelineAnalyticsCapability = {
19834
20000
  kind: "mutation",
19835
20001
  auth: "admin"
19836
20002
  }),
20003
+ /**
20004
+ * Moves event media between locations — the OPERATOR's mover, without the
20005
+ * coordinator's lease. Twin of `recording.relocateFootage`: the same
20006
+ * engine the lease-gated coordinated migration uses
20007
+ * (`startStorageMigrationMove`), armable in the background and WITHOUT
20008
+ * pausing anything. Exists because `eventMedia` was the only storage
20009
+ * class whose only move path went through the recorder's global pause.
20010
+ */
20011
+ relocateMedia: method(RelocateMediaInputSchema, object({ jobId: string() }), {
20012
+ kind: "mutation",
20013
+ auth: "admin"
20014
+ }),
20015
+ /** Every relocate job this addon knows about, newest first (in RAM: the
20016
+ * move is resumable, so a lost list costs nothing but the display). */
20017
+ listRelocateMediaJobs: method(object({}), array(RelocateJobSchema).readonly(), {
20018
+ kind: "query",
20019
+ auth: "admin"
20020
+ }),
20021
+ /** Cancel a running or queued relocate job. */
20022
+ cancelRelocateMedia: method(object({ jobId: string() }), object({ cancelled: boolean() }), {
20023
+ kind: "mutation",
20024
+ auth: "admin"
20025
+ }),
19837
20026
  listOpsLog: method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19838
20027
  kind: "query",
19839
20028
  auth: "admin"
@@ -20193,6 +20382,11 @@ object({
20193
20382
  "jpeg"
20194
20383
  ])
20195
20384
  });
20385
+ /**
20386
+ * Process-local frame identity. It is serializable so it can ride an in-process
20387
+ * capability call, but `registryId` deliberately prevents resolution in any
20388
+ * other process or execution group.
20389
+ */
20196
20390
  var FrameRefSchema = object({
20197
20391
  registryId: string().min(1),
20198
20392
  id: string().min(1),
@@ -23146,6 +23340,20 @@ var ssoBridgeCapability = {
23146
23340
  mode: "singleton",
23147
23341
  internal: true,
23148
23342
  methods: {
23343
+ /**
23344
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
23345
+ * `data-store-provider`) — `signBridgeToken` was reachable on the
23346
+ * AppRouter by ANY authenticated session at the default
23347
+ * `auth: 'protected'`. The claims (including `isAdmin`) are CALLER-
23348
+ * SUPPLIED: an unprivileged session could have minted its own
23349
+ * `isAdmin: true` bridge token and redeemed it at
23350
+ * `/api/auth/sso/finish` to become admin — a full privilege escalation
23351
+ * through the exact surface this cap exists to close (see the module
23352
+ * docblock). Every real caller (`oauth-grants.ts`, `main.ts`
23353
+ * registration, the OIDC/magic-link addons) reaches this through
23354
+ * `ctx.api` / the in-process registry — never through tRPC — so
23355
+ * `auth: 'admin'` costs those callers nothing.
23356
+ */
23149
23357
  signBridgeToken: method(object({
23150
23358
  claims: SsoBridgeClaimsSchema,
23151
23359
  /**
@@ -23158,8 +23366,8 @@ var ssoBridgeCapability = {
23158
23366
  * linking rather than produce an eternal token.
23159
23367
  */
23160
23368
  ttlSec: union([number().int().positive(), literal("never")]).optional()
23161
- }), object({ token: string() })),
23162
- verifyBridgeToken: method(object({ token: string() }), SsoBridgeClaimsSchema.nullable())
23369
+ }), object({ token: string() }), { auth: "admin" }),
23370
+ verifyBridgeToken: method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" })
23163
23371
  }
23164
23372
  };
23165
23373
  var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
@@ -23308,8 +23516,15 @@ var storageEvictableCapability = {
23308
23516
  mode: "collection",
23309
23517
  internal: true,
23310
23518
  methods: {
23519
+ /**
23520
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
23521
+ * `data-store-provider`) — this was reachable on the AppRouter by ANY
23522
+ * authenticated session at the default `auth: 'protected'`. The only
23523
+ * consumer is the orchestrator's `StoragePressureManager`, which calls
23524
+ * `ctx.api` — never tRPC — so `auth: 'admin'` costs it nothing.
23525
+ */
23311
23526
  /** Bytes this provider holds on the given location — drives proportional fan-out. */
23312
- getEvictableUsage: method(object({ locationId: string() }), EvictableUsageSchema),
23527
+ getEvictableUsage: method(object({ locationId: string() }), EvictableUsageSchema, { auth: "admin" }),
23313
23528
  /**
23314
23529
  * Free approximately `targetBytes` of this provider's OWN least-valuable
23315
23530
  * data on the location (oldest footage, expired clips, …). Returns what it
@@ -23318,7 +23533,10 @@ var storageEvictableCapability = {
23318
23533
  evict: method(object({
23319
23534
  locationId: string(),
23320
23535
  targetBytes: number().int().positive()
23321
- }), EvictResultSchema, { kind: "mutation" })
23536
+ }), EvictResultSchema, {
23537
+ kind: "mutation",
23538
+ auth: "admin"
23539
+ })
23322
23540
  }
23323
23541
  };
23324
23542
  /**
@@ -23421,43 +23639,82 @@ var storageProviderCapability = {
23421
23639
  mode: "collection",
23422
23640
  internal: true,
23423
23641
  methods: {
23642
+ /**
23643
+ * Every method below except `testLocation` had NO `auth` override —
23644
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
23645
+ * `data-store-provider`), so this was reachable on the AppRouter by ANY
23646
+ * authenticated session at the default `auth: 'protected'`: arbitrary
23647
+ * read/write/delete/list against any storage location the operator has
23648
+ * configured, keyed only by a caller-supplied `location` + `relativePath`
23649
+ * — the same "public consumers never see this cap" claim that was false
23650
+ * for `data-store-provider`, here gating raw file I/O instead of raw
23651
+ * settings rows. The only legitimate caller,
23652
+ * `addon-registry.service.ts`'s `activeStorageProvider`, calls the
23653
+ * provider object directly (in-process) — never tRPC — so `auth: 'admin'`
23654
+ * on every method costs it nothing. Public consumers already go through
23655
+ * the separate `storage` singleton cap.
23656
+ */
23424
23657
  /** Self-description for the "Add location" wizard. */
23425
- getProviderInfo: method(_void(), ProviderInfoSchema),
23658
+ getProviderInfo: method(_void(), ProviderInfoSchema, { auth: "admin" }),
23426
23659
  /** Validate config + probe connectivity. Called on add and on edit. */
23427
23660
  testLocation: method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }),
23428
23661
  resolve: method(object({
23429
23662
  location: StorageLocationSchema,
23430
23663
  relativePath: string()
23431
- }), string()),
23664
+ }), string(), { auth: "admin" }),
23432
23665
  write: method(object({
23433
23666
  location: StorageLocationSchema,
23434
23667
  relativePath: string(),
23435
23668
  data: _instanceof(Uint8Array)
23436
- }), _void(), { kind: "mutation" }),
23669
+ }), _void(), {
23670
+ kind: "mutation",
23671
+ auth: "admin"
23672
+ }),
23437
23673
  read: method(object({
23438
23674
  location: StorageLocationSchema,
23439
23675
  relativePath: string()
23440
- }), _instanceof(Uint8Array)),
23676
+ }), _instanceof(Uint8Array), { auth: "admin" }),
23441
23677
  exists: method(object({
23442
23678
  location: StorageLocationSchema,
23443
23679
  relativePath: string()
23444
- }), boolean()),
23680
+ }), boolean(), { auth: "admin" }),
23445
23681
  list: method(object({
23446
23682
  location: StorageLocationSchema,
23447
23683
  prefix: string().optional()
23448
- }), array(string()).readonly()),
23684
+ }), array(string()).readonly(), { auth: "admin" }),
23449
23685
  delete: method(object({
23450
23686
  location: StorageLocationSchema,
23451
23687
  relativePath: string()
23452
- }), _void(), { kind: "mutation" }),
23453
- getAvailableSpace: method(object({ location: StorageLocationSchema }), number().nullable()),
23454
- beginUpload: method(BeginUploadInputSchema, BeginUploadResultSchema, { kind: "mutation" }),
23455
- writeChunk: method(WriteChunkInputSchema, _void(), { kind: "mutation" }),
23456
- finalizeUpload: method(FinalizeUploadInputSchema, _void(), { kind: "mutation" }),
23457
- abortUpload: method(AbortUploadInputSchema, _void(), { kind: "mutation" }),
23458
- beginDownload: method(BeginDownloadInputSchema, BeginDownloadResultSchema, { kind: "mutation" }),
23459
- readChunk: method(ReadChunkInputSchema, _instanceof(Uint8Array)),
23460
- endDownload: method(EndDownloadInputSchema, _void(), { kind: "mutation" })
23688
+ }), _void(), {
23689
+ kind: "mutation",
23690
+ auth: "admin"
23691
+ }),
23692
+ getAvailableSpace: method(object({ location: StorageLocationSchema }), number().nullable(), { auth: "admin" }),
23693
+ beginUpload: method(BeginUploadInputSchema, BeginUploadResultSchema, {
23694
+ kind: "mutation",
23695
+ auth: "admin"
23696
+ }),
23697
+ writeChunk: method(WriteChunkInputSchema, _void(), {
23698
+ kind: "mutation",
23699
+ auth: "admin"
23700
+ }),
23701
+ finalizeUpload: method(FinalizeUploadInputSchema, _void(), {
23702
+ kind: "mutation",
23703
+ auth: "admin"
23704
+ }),
23705
+ abortUpload: method(AbortUploadInputSchema, _void(), {
23706
+ kind: "mutation",
23707
+ auth: "admin"
23708
+ }),
23709
+ beginDownload: method(BeginDownloadInputSchema, BeginDownloadResultSchema, {
23710
+ kind: "mutation",
23711
+ auth: "admin"
23712
+ }),
23713
+ readChunk: method(ReadChunkInputSchema, _instanceof(Uint8Array), { auth: "admin" }),
23714
+ endDownload: method(EndDownloadInputSchema, _void(), {
23715
+ kind: "mutation",
23716
+ auth: "admin"
23717
+ })
23461
23718
  }
23462
23719
  };
23463
23720
  /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
@@ -23790,9 +24047,28 @@ var userPasskeysCapability = {
23790
24047
  auth: "admin",
23791
24048
  access: "create"
23792
24049
  }),
24050
+ /**
24051
+ * The four methods below had NO `auth` override — `internal: true` did
24052
+ * not gate the mount (see the 2026-08-26 note on `data-store-provider`),
24053
+ * so they were reachable on the AppRouter at the default
24054
+ * `auth: 'protected'` (any authenticated session), inconsistent with
24055
+ * every other method on this cap (all already `auth: 'admin'`).
24056
+ *
24057
+ * ⚠ Checked before gating: these are also the PRE-AUTH login-ceremony
24058
+ * methods, so admin-gating them here must not break login for an
24059
+ * unauthenticated principal. It doesn't — the real login path is the
24060
+ * PUBLIC `auth.beginAuthentication` / `auth.finishAuthentication` /
24061
+ * `auth.beginDiscoverableAuthentication` / `auth.finishDiscoverableAuthentication`
24062
+ * procedures in `server/backend/src/api/core/auth.router.ts`, which call
24063
+ * `provider.beginAuthentication(...)` etc. directly against the
24064
+ * capability-registry-resolved provider — never through this cap's own
24065
+ * tRPC route. `auth: 'admin'` here only closes a redundant, unused
24066
+ * second entry point onto the same ceremony.
24067
+ */
23793
24068
  beginAuthentication: method(object({ userId: string().optional() }), object({ optionsJSON: record(string(), unknown()) }), {
23794
24069
  kind: "mutation",
23795
- access: "view"
24070
+ access: "view",
24071
+ auth: "admin"
23796
24072
  }),
23797
24073
  finishAuthentication: method(object({
23798
24074
  /** Required — the user the assertion belongs to (verified). */
@@ -23801,11 +24077,13 @@ var userPasskeysCapability = {
23801
24077
  response: record(string(), unknown())
23802
24078
  }), object({ verified: boolean() }), {
23803
24079
  kind: "mutation",
23804
- access: "view"
24080
+ access: "view",
24081
+ auth: "admin"
23805
24082
  }),
23806
24083
  beginDiscoverableAuthentication: method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
23807
24084
  kind: "mutation",
23808
- access: "view"
24085
+ access: "view",
24086
+ auth: "admin"
23809
24087
  }),
23810
24088
  finishDiscoverableAuthentication: method(object({
23811
24089
  /** AuthenticationResponseJSON from the browser. */
@@ -23814,7 +24092,8 @@ response: record(string(), unknown()) }), object({
23814
24092
  userId: string().nullable()
23815
24093
  }), {
23816
24094
  kind: "mutation",
23817
- access: "view"
24095
+ access: "view",
24096
+ auth: "admin"
23818
24097
  }),
23819
24098
  listPasskeys: method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }),
23820
24099
  removePasskey: method(object({
@@ -23992,6 +24271,14 @@ var VectorStatsResultSchema = object({
23992
24271
  /** False when the backend ranks approximately. */
23993
24272
  exact: boolean()
23994
24273
  });
24274
+ /**
24275
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
24276
+ * `data-store-provider`) — every method below was reachable on the AppRouter
24277
+ * by ANY authenticated session at the default `auth: 'protected'`. The only
24278
+ * consumers (`addon-post-analysis`'s embedding stores) reach this cap via
24279
+ * `ctx.api` — UDS/Moleculer, never tRPC — so `auth: 'admin'` on every method
24280
+ * costs them nothing. No client (viewer or admin-ui) calls `vectorStore.*`.
24281
+ */
23995
24282
  var vectorStoreCapability = {
23996
24283
  name: "vector-store",
23997
24284
  scope: "system",
@@ -23999,14 +24286,26 @@ var vectorStoreCapability = {
23999
24286
  internal: true,
24000
24287
  methods: {
24001
24288
  /** Idempotent. Re-declaring with a different `dim` is an error, not a wipe. */
24002
- declareIndex: method(VectorDeclareIndexInputSchema, _void(), { kind: "mutation" }),
24003
- upsert: method(VectorUpsertInputSchema, VectorUpsertResultSchema, { kind: "mutation" }),
24004
- query: method(VectorQueryInputSchema, VectorQueryResultSchema),
24289
+ declareIndex: method(VectorDeclareIndexInputSchema, _void(), {
24290
+ kind: "mutation",
24291
+ auth: "admin"
24292
+ }),
24293
+ upsert: method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
24294
+ kind: "mutation",
24295
+ auth: "admin"
24296
+ }),
24297
+ query: method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }),
24005
24298
  /** Metadata by id, no vectors — see {@link VectorGetResultSchema}. */
24006
- getByIds: method(VectorGetInputSchema, VectorGetResultSchema),
24007
- deleteByIds: method(VectorDeleteInputSchema, VectorDeleteResultSchema, { kind: "mutation" }),
24008
- deleteByFilter: method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, { kind: "mutation" }),
24009
- stats: method(VectorStatsInputSchema, VectorStatsResultSchema)
24299
+ getByIds: method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }),
24300
+ deleteByIds: method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
24301
+ kind: "mutation",
24302
+ auth: "admin"
24303
+ }),
24304
+ deleteByFilter: method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
24305
+ kind: "mutation",
24306
+ auth: "admin"
24307
+ }),
24308
+ stats: method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" })
24010
24309
  }
24011
24310
  };
24012
24311
  var ClipSchema = object({
@@ -26855,7 +27154,27 @@ var faceGalleryCapability = {
26855
27154
  scope: "system",
26856
27155
  mode: "singleton",
26857
27156
  methods: {
26858
- listIdentities: method(_void(), array(IdentitySchema).readonly()),
27157
+ listIdentities: method(object({
27158
+ /**
27159
+ * Inline {@link IdentitySchema.coverBase64} on every row.
27160
+ *
27161
+ * Default `false`, the same inversion `listRecentFaces` and
27162
+ * `listPlates` took on 2026-08-25 (see `include-crops-default.ts` for
27163
+ * why the burden belongs on the caller that WANTS the bytes). Measured
27164
+ * on the live hub the same day: four identities cost 40,979 B with the
27165
+ * covers inline, ~10 KB of base64 per row, on a query this UI mounts
27166
+ * four times and the viewer holds at `staleTime: 30_000`.
27167
+ *
27168
+ * Nothing loses its avatar: `coverMediaKey` is already on every row and
27169
+ * the `event-media` plane serves that key `immutable` with an ETag.
27170
+ *
27171
+ * **This is an INPUT field, so it does not reach the addon until the
27172
+ * next train** — the hub router validates cap inputs against its own
27173
+ * compiled Zod and strips a key it does not know. Until then the
27174
+ * provider sees `undefined`, which resolves to `false`: the cheap shape
27175
+ * is what ships, and the opt-in becomes reachable when the train lands.
27176
+ */
27177
+ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly()),
26859
27178
  createIdentity: method(object({ name: string().min(1) }), IdentitySchema, {
26860
27179
  kind: "mutation",
26861
27180
  auth: "admin"
@@ -30108,8 +30427,10 @@ var PlateInfoSchema = object({
30108
30427
  keyFrameMediaKey: string().optional(),
30109
30428
  base64: string().optional(),
30110
30429
  /**
30111
- * Same crop as a data-plane URL. `getPlateByTrack` returns this and
30112
- * never inlines JPEG; `listPlates` still inlines for the admin-ui.
30430
+ * Same crop as a data-plane URL, always present when the plate has a stored
30431
+ * crop. `getPlateByTrack` returns this and never inlines JPEG; `listPlates`
30432
+ * and `searchPlates` inline the crop only when their `includeCrops` input is
30433
+ * left at its `true` default.
30113
30434
  */
30114
30435
  cropUrl: string().optional()
30115
30436
  });
@@ -30142,7 +30463,25 @@ var plateGalleryCapability = {
30142
30463
  methods: {
30143
30464
  listPlates: method(object({
30144
30465
  deviceId: number().int().optional(),
30145
- limit: number().int().positive().optional()
30466
+ limit: number().int().positive().optional(),
30467
+ /**
30468
+ * Inline the base64 crop on every row. Default `true` — the existing
30469
+ * behaviour, kept so no caller breaks.
30470
+ *
30471
+ * Set `false` once the caller renders {@link PlateInfo.cropUrl}.
30472
+ * Measured on the live hub at the 500 rows the Plates view asks for:
30473
+ * 879,403 B and 27.7 s with the crops inline, against a few KiB of
30474
+ * metadata without them — and the browser then caches the images.
30475
+ *
30476
+ * This is the plate twin of `faceGallery.listRecentFaces`'s option;
30477
+ * plates were the one gallery list left without it.
30478
+ *
30479
+ * **This is an INPUT field, so it does not reach the addon until the
30480
+ * next train.** The hub router validates cap inputs against its own
30481
+ * compiled Zod and strips a key it does not know. Until the train
30482
+ * ships, sending `false` is harmless and keeps the crops inline.
30483
+ */
30484
+ includeCrops: boolean().optional()
30146
30485
  }).optional(), array(PlateInfoSchema).readonly()),
30147
30486
  getPlateByTrack: method(object({
30148
30487
  deviceId: number().int(),
@@ -30153,7 +30492,9 @@ var plateGalleryCapability = {
30153
30492
  searchPlates: method(object({
30154
30493
  text: string().min(1),
30155
30494
  maxDistance: number().int().min(0).optional(),
30156
- limit: number().int().positive().optional()
30495
+ limit: number().int().positive().optional(),
30496
+ /** See `listPlates.includeCrops`. Default `true`. */
30497
+ includeCrops: boolean().optional()
30157
30498
  }), array(PlateInfoSchema).readonly()),
30158
30499
  /** Suggest groups of near-identical reads (same physical plate) for review/merge. */
30159
30500
  suggestPlateClusters: method(object({
@@ -30173,7 +30514,13 @@ var plateGalleryCapability = {
30173
30514
  kind: "mutation",
30174
30515
  auth: "admin"
30175
30516
  }),
30176
- listVehicles: method(_void(), array(VehicleSchema).readonly()),
30517
+ listVehicles: method(object({
30518
+ /** Inline {@link VehicleSchema.coverBase64} on every row. Default
30519
+ * `false` — the vehicle twin of `faceGallery.listIdentities`'s
30520
+ * option; `coverMediaKey` + the `event-media` plane carry the picture.
30521
+ * INPUT field: stripped by the hub router until the train ships, which
30522
+ * resolves to `false` and is exactly the intended default. */
30523
+ includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()),
30177
30524
  createVehicle: method(object({ name: string().min(1) }), VehicleSchema, {
30178
30525
  kind: "mutation",
30179
30526
  auth: "admin"
@@ -36917,6 +37264,12 @@ Object.freeze({
36917
37264
  addonId: null,
36918
37265
  access: "create"
36919
37266
  },
37267
+ "pipelineAnalytics.cancelRelocateMedia": {
37268
+ capName: "pipeline-analytics",
37269
+ capScope: "device",
37270
+ addonId: null,
37271
+ access: "create"
37272
+ },
36920
37273
  "pipelineAnalytics.cancelStorageMigrationMove": {
36921
37274
  capName: "pipeline-analytics",
36922
37275
  capScope: "device",
@@ -37091,6 +37444,12 @@ Object.freeze({
37091
37444
  addonId: null,
37092
37445
  access: "view"
37093
37446
  },
37447
+ "pipelineAnalytics.listRelocateMediaJobs": {
37448
+ capName: "pipeline-analytics",
37449
+ capScope: "device",
37450
+ addonId: null,
37451
+ access: "view"
37452
+ },
37094
37453
  "pipelineAnalytics.listRetrainAnnotations": {
37095
37454
  capName: "pipeline-analytics",
37096
37455
  capScope: "device",
@@ -37169,6 +37528,12 @@ Object.freeze({
37169
37528
  addonId: null,
37170
37529
  access: "create"
37171
37530
  },
37531
+ "pipelineAnalytics.relocateMedia": {
37532
+ capName: "pipeline-analytics",
37533
+ capScope: "device",
37534
+ addonId: null,
37535
+ access: "create"
37536
+ },
37172
37537
  "pipelineAnalytics.restageRetrainTrack": {
37173
37538
  capName: "pipeline-analytics",
37174
37539
  capScope: "device",