@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.mjs CHANGED
@@ -6629,7 +6629,7 @@ function method(input, output, options) {
6629
6629
  input,
6630
6630
  output,
6631
6631
  kind: options?.kind ?? "query",
6632
- auth: options?.auth ?? "protected",
6632
+ ...options?.auth !== void 0 ? { auth: options.auth } : {},
6633
6633
  ...options?.access !== void 0 ? { access: options.access } : {},
6634
6634
  ...options?.caller !== void 0 ? { caller: options.caller } : {},
6635
6635
  timeoutMs: options?.timeoutMs
@@ -6659,8 +6659,17 @@ var adminUiCapability = {
6659
6659
  mode: "singleton",
6660
6660
  internal: true,
6661
6661
  methods: {
6662
- getStaticDir: method(_void(), StaticDirOutputSchema$1),
6663
- getVersion: method(_void(), VersionOutputSchema$1)
6662
+ /**
6663
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
6664
+ * `data-store-provider`) — both methods were reachable on the AppRouter
6665
+ * by ANY authenticated session at the default `auth: 'protected'`, and
6666
+ * `getStaticDir` leaks a hub filesystem path. The only caller is
6667
+ * `main.ts`'s static-file bootstrap, via `capRegistry.getSingletonForNode`
6668
+ * — never tRPC — so `auth: 'admin'` costs it nothing. The actual admin-ui
6669
+ * SPA assets are served as plain static files, unaffected by this gate.
6670
+ */
6671
+ getStaticDir: method(_void(), StaticDirOutputSchema$1, { auth: "admin" }),
6672
+ getVersion: method(_void(), VersionOutputSchema$1, { auth: "admin" })
6664
6673
  }
6665
6674
  };
6666
6675
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
@@ -6996,8 +7005,23 @@ var viewerUiCapability = {
6996
7005
  mode: "singleton",
6997
7006
  internal: true,
6998
7007
  methods: {
6999
- getStaticDir: method(_void(), StaticDirOutputSchema),
7000
- getVersion: method(_void(), VersionOutputSchema)
7008
+ /**
7009
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
7010
+ * `data-store-provider`) — both methods were reachable on the AppRouter
7011
+ * by ANY authenticated session at the default `auth: 'protected'`, and
7012
+ * `getStaticDir` leaks a hub filesystem path.
7013
+ *
7014
+ * ⚠ Checked before gating: the VIEWER app IS used by a non-admin user
7015
+ * (`roberta`), but it never calls `viewerUi.*` over tRPC. The static SPA
7016
+ * it loads is served by the plain `GET /viewer/*` Fastify route in
7017
+ * `main.ts`, which reads `staticDir`/`indexPath` from a module-level
7018
+ * cache populated ONCE at boot via `capRegistry.getSingletonForNode`
7019
+ * (in-process, not tRPC) — the cap's tRPC procedure is never on that
7020
+ * request path. `auth: 'admin'` here does not block a single viewer
7021
+ * user from loading the app.
7022
+ */
7023
+ getStaticDir: method(_void(), StaticDirOutputSchema, { auth: "admin" }),
7024
+ getVersion: method(_void(), VersionOutputSchema, { auth: "admin" })
7001
7025
  }
7002
7026
  };
7003
7027
  /**
@@ -7672,24 +7696,6 @@ var RecordingRetentionSchema = object({
7672
7696
  maxSizeGb: number().min(0).optional()
7673
7697
  });
7674
7698
  /**
7675
- * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7676
- * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7677
- * previews at. Five graduated steps; absent on a config = `standard` (the
7678
- * shipped default, matching `sheet-geometry`/`sheet-composer`).
7679
- *
7680
- * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7681
- * Each window's index sidecar carries its own tile dims, so a camera whose
7682
- * preset changed over time renders every historical window at the dims it was
7683
- * written with.
7684
- */
7685
- var ScrubThumbnailPresetSchema = _enum([
7686
- "minimal",
7687
- "low",
7688
- "standard",
7689
- "high",
7690
- "max"
7691
- ]);
7692
- /**
7693
7699
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7694
7700
  *
7695
7701
  * `bands` is the ONLY authored recording intent: what to record, when, and on
@@ -7697,7 +7703,11 @@ var ScrubThumbnailPresetSchema = _enum([
7697
7703
  * other field is a storage knob (profiles, segment length, retention, scrub).
7698
7704
  *
7699
7705
  * STRICT on purpose: the legacy authoring surface (`schedule`/`schedules`/
7700
- * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30.
7706
+ * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30,
7707
+ * and `scrubThumbnails` — a five-step fidelity knob for a sprite tier that was
7708
+ * deleted on 2026-07-24 and had ZERO consumers in the recorder — on 2026-08-25
7709
+ * (D62: a switch that writes a store nobody reads is worse than no switch).
7710
+ * Stored rows keep loading: the READ schema is `.strip()` (config-store.ts).
7701
7711
  * A stale caller must fail loudly — silently stripping its legacy intent would
7702
7712
  * persist a band-less config, i.e. silently stop recording the camera.
7703
7713
  */
@@ -7720,14 +7730,7 @@ var RecordingConfigSchema = object({
7720
7730
  * "off" is the absence of a covering band, never a band value.
7721
7731
  */
7722
7732
  bands: array(RecordingBandSchema).default([]),
7723
- retention: RecordingRetentionSchema.optional(),
7724
- /**
7725
- * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7726
- * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7727
- * windows only — existing sheets are immutable, and each window's index
7728
- * carries its own tile dims so mixed-preset history renders correctly.
7729
- */
7730
- scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7733
+ retention: RecordingRetentionSchema.optional()
7731
7734
  }).strict();
7732
7735
  /**
7733
7736
  * Entity-relocation job state (storage entity-routing spec, Phase 4).
@@ -7803,10 +7806,11 @@ var RelocateFootageInputSchema = object({
7803
7806
  * `RecordingConfig.enabled` or camera wrapper bindings. */
7804
7807
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
7805
7808
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
7806
- var StorageMigrationMediaMoveInputSchema = object({
7809
+ var RelocateMediaInputSchema = object({
7807
7810
  toLocationId: string(),
7808
7811
  throttleMbps: number().min(1).max(1e3).optional()
7809
- }).extend({ leaseId: string().min(1) });
7812
+ });
7813
+ var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
7810
7814
  /** The independently selectable logical storage classes. `recordings`
7811
7815
  * encompasses the high and mid segment profiles; `recordingsLow` is low
7812
7816
  * segments; `eventMedia` is post-analysis blobs. */
@@ -8101,7 +8105,26 @@ var LabelDefinitionSchema = object({
8101
8105
  description: string().optional(),
8102
8106
  icon: string().optional()
8103
8107
  });
8104
- var ClassMapDefinitionSchema = object({
8108
+ /**
8109
+ * Wire schema for a per-model CATALOG classMap override
8110
+ * (`ModelCatalogEntry.classMap` / `ModelConvertMetadata.classMap`) —
8111
+ * restricted to {@link CLASS_MAP_MACRO_TARGETS}, the only macros the
8112
+ * detection pipeline executor actually routes.
8113
+ *
8114
+ * This is deliberately a DIFFERENT, narrower shape than the general-purpose
8115
+ * `ClassMapDefinition` interface above (e.g. `IDetectionAddon.getClassMap()`
8116
+ * and the audio `YAMNET_TO_MACRO` catalog both use macro targets outside this
8117
+ * enum) — the two used to share the name `ClassMapDefinition`/
8118
+ * `ClassMapDefinitionSchema`, which made the schema-type-twin guard
8119
+ * (`scripts/check-schema-type-twins.ts`) flag them as a duplicated shape. They
8120
+ * are not: it is two different concepts colliding on a name. Keep this type
8121
+ * under its own name rather than reusing `ClassMapDefinition` — reusing it
8122
+ * would either narrow every `ClassMapDefinition` consumer to the four
8123
+ * detection macros (breaking `YAMNET_TO_MACRO`) or drop the validation this
8124
+ * schema exists for (see the "rejects a classMap whose target is not a
8125
+ * detection macro" test in `model-catalog-schema.test.ts`).
8126
+ */
8127
+ var DetectionCatalogClassMapSchema = object({
8105
8128
  mapping: record(string(), _enum([
8106
8129
  "person",
8107
8130
  "vehicle",
@@ -8306,7 +8329,7 @@ var ModelCatalogEntrySchema = object({
8306
8329
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8307
8330
  * labels already ARE the CamStack macros (Scrypted identity map).
8308
8331
  */
8309
- classMap: ClassMapDefinitionSchema.optional()
8332
+ classMap: DetectionCatalogClassMapSchema.optional()
8310
8333
  });
8311
8334
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8312
8335
  format: literal("openvino"),
@@ -8336,7 +8359,7 @@ var ModelConvertMetadataSchema = object({
8336
8359
  "segmentation"
8337
8360
  ]),
8338
8361
  faceAlignment: boolean().optional(),
8339
- classMap: ClassMapDefinitionSchema.optional()
8362
+ classMap: DetectionCatalogClassMapSchema.optional()
8340
8363
  });
8341
8364
  var ConvertResultSchema = object({
8342
8365
  entry: ModelCatalogEntrySchema,
@@ -9209,7 +9232,18 @@ var addonPagesSourceCapability = {
9209
9232
  scope: "system",
9210
9233
  mode: "collection",
9211
9234
  internal: true,
9212
- methods: { listPages: method(_void(), array(AddonPageDeclarationSchema).readonly()) }
9235
+ methods: {
9236
+ /**
9237
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
9238
+ * `data-store-provider`) — this was reachable on the AppRouter by ANY
9239
+ * authenticated session at the default `auth: 'protected'`. The only
9240
+ * consumer, the `addon-pages-aggregator` builtin, reads it via
9241
+ * `ctx.capabilities.getCollection('addon-pages-source')` — LOCAL-PROCESS
9242
+ * only, never tRPC (per `ctx.capabilities` seeing only the local
9243
+ * process). The public, enriched listing admin-ui actually consumes is
9244
+ * the separate `addon-pages` cap, unaffected by this change.
9245
+ */
9246
+ listPages: method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" }) }
9213
9247
  };
9214
9248
  var AddonHttpRouteSchema = object({
9215
9249
  method: _enum([
@@ -9506,7 +9540,20 @@ var addonWidgetsSourceCapability = {
9506
9540
  scope: "system",
9507
9541
  mode: "collection",
9508
9542
  internal: true,
9509
- methods: { listWidgets: method(_void(), array(WidgetMetadataSchema).readonly()) }
9543
+ methods: {
9544
+ /**
9545
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
9546
+ * `data-store-provider`) — this was reachable on the AppRouter by ANY
9547
+ * authenticated session at the default `auth: 'protected'`. The only
9548
+ * consumer, the `addon-widgets-aggregator` builtin, reads it via
9549
+ * `ctx.capabilities.getCollection('addon-widgets-source')` — LOCAL-PROCESS
9550
+ * only, never tRPC. The public, enriched listing admin-ui actually
9551
+ * consumes is the separate `addon-widgets` cap (`listWidgets` there is
9552
+ * gated separately, and a `preAuth: true` widget is surfaced through the
9553
+ * PUBLIC `auth.listLoginMethods` contribution channel instead) —
9554
+ * neither is affected by this change.
9555
+ */
9556
+ listWidgets: method(_void(), array(WidgetMetadataSchema).readonly(), { auth: "admin" }) }
9510
9557
  };
9511
9558
  /**
9512
9559
  * `addon-widgets` — system-scoped singleton aggregator cap. Public-facing
@@ -11670,7 +11717,17 @@ var customModelRegistryCapability = {
11670
11717
  scope: "system",
11671
11718
  mode: "collection",
11672
11719
  internal: true,
11673
- methods: { listModels: method(_void(), array(CustomModelDescriptorSchema).readonly()) }
11720
+ methods: {
11721
+ /**
11722
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
11723
+ * `data-store-provider`) — this was reachable on the AppRouter by ANY
11724
+ * authenticated session at the default `auth: 'protected'`. The only
11725
+ * consumer, the detection-pipeline's model resolution
11726
+ * (`addon-pipeline/src/detection-pipeline/provider.ts`), calls
11727
+ * `ctx.api.customModelRegistry.listModels.query()` — UDS/Moleculer,
11728
+ * never tRPC — so `auth: 'admin'` costs it nothing.
11729
+ */
11730
+ listModels: method(_void(), array(CustomModelDescriptorSchema).readonly(), { auth: "admin" }) }
11674
11731
  };
11675
11732
  /**
11676
11733
  * Query filter for settings-store collections.
@@ -11794,11 +11851,26 @@ var settingsStoreCapability = {
11794
11851
  key: string(),
11795
11852
  value: unknown()
11796
11853
  }), _void(), { kind: "mutation" }),
11797
- /** Get all entries matching an optional filter. */
11854
+ /**
11855
+ * Rows matching a filter.
11856
+ *
11857
+ * `columns` is a SQL-level projection: the engine SELECTs only those
11858
+ * columns (plus the primary key) instead of the whole row. It is the only
11859
+ * way to stop paying disk for a column that is discarded — a heavy JSON
11860
+ * column dominates the row, and dropping it in JS after the read pays every
11861
+ * byte first. Measured on the live `pipeline-analytics:tracks` table: a
11862
+ * 501-row page is 4.6 MB whole and 0.11 MB projected, same index, same
11863
+ * plan.
11864
+ *
11865
+ * The engine REFUSES a column it cannot serve rather than omitting it —
11866
+ * see `SettingsQueryInput.columns` for why a projection may not be
11867
+ * forgiving the way a predicate is.
11868
+ */
11798
11869
  query: method(object({
11799
11870
  namespace: string().optional(),
11800
11871
  collection: string(),
11801
- filter: QueryFilterSchema.optional()
11872
+ filter: QueryFilterSchema.optional(),
11873
+ columns: array(string()).readonly().optional()
11802
11874
  }), array(SettingsRecordSchema).readonly()),
11803
11875
  /** Insert a new record. */
11804
11876
  insert: method(object({
@@ -11924,6 +11996,23 @@ var EngineInfoSchema = object({
11924
11996
  * go through `settings-store`; they never see this cap, and an engine
11925
11997
  * never sees a caller.
11926
11998
  *
11999
+ * ⚠ **`internal: true` DOES NOT gate the mount, and until 2026-08-26 this
12000
+ * comment claimed otherwise.** `resolveCapMount` never reads the field, so this
12001
+ * cap was live on the AppRouter with every method at the default
12002
+ * `auth: 'protected'` — i.e. readable by ANY authenticated session, admin or
12003
+ * not. Verified against the live hub: `dataStoreProvider.query` returned raw
12004
+ * settings rows including a provider credential, bypassing the secret redaction
12005
+ * that `deviceManager.listAll` and friends go through.
12006
+ *
12007
+ * Every method is now `auth: 'admin'` EXPLICITLY, including the five reads —
12008
+ * they had no options object at all, which is exactly how they kept the
12009
+ * permissive default while the mutations looked deliberate. The explicit
12010
+ * annotation is the fix that works today; making `internal` mean something at
12011
+ * mount time is the systemic one, and it is not free: 22 caps declare it, only
12012
+ * 4 are `mount: 'skip'`, and forcing admin on all of them would change the auth
12013
+ * of surfaces the viewer and admin-ui call as non-admin users. That decision is
12014
+ * the operator's, not a side effect of this file.
12015
+ *
11927
12016
  * Design notes:
11928
12017
  * - **Stateless dispatch.** Every method carries its own
11929
12018
  * `namespace` + `collection`, so an engine keeps no per-caller state
@@ -11945,64 +12034,105 @@ var dataStoreProviderCapability = {
11945
12034
  internal: true,
11946
12035
  methods: {
11947
12036
  /** Self-description — how the orchestrator picks a registrant. */
11948
- getEngineInfo: method(_void(), EngineInfoSchema),
12037
+ getEngineInfo: method(_void(), EngineInfoSchema, { auth: "admin" }),
11949
12038
  /** Get a single value by key from a collection. */
11950
12039
  get: method(object({
11951
12040
  namespace: string().optional(),
11952
12041
  collection: string(),
11953
12042
  key: string()
11954
- }), unknown()),
12043
+ }), unknown(), { auth: "admin" }),
11955
12044
  /** Set a value by key in a collection (upsert). */
11956
12045
  set: method(object({
11957
12046
  namespace: string().optional(),
11958
12047
  collection: string(),
11959
12048
  key: string(),
11960
12049
  value: unknown()
11961
- }), _void(), { kind: "mutation" }),
12050
+ }), _void(), {
12051
+ kind: "mutation",
12052
+ auth: "admin"
12053
+ }),
11962
12054
  /** Get all entries matching an optional filter. */
11963
12055
  query: method(object({
11964
12056
  namespace: string().optional(),
11965
12057
  collection: string(),
11966
- filter: QueryFilterSchema.optional()
11967
- }), array(SettingsRecordSchema).readonly()),
12058
+ filter: QueryFilterSchema.optional(),
12059
+ /**
12060
+ * SQL-level column projection — MUST mirror `settings-store.query`.
12061
+ *
12062
+ * ⚠ An earlier version of this comment blamed Zod stripping, and that
12063
+ * was wrong — corrected 2026-08-26 after the hop map
12064
+ * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
12065
+ * There is **no Zod parse at all** between the door and the engine: the
12066
+ * dispatcher forwards the payload verbatim and UDS carries it whole. A
12067
+ * field declared here reaches `SqliteSettingsBackend` either way.
12068
+ *
12069
+ * What actually lost `columns` was the THIRD declaration of this shape:
12070
+ * `SettingsQueryInput` in `interfaces/storage.ts`, a hand-written TS
12071
+ * interface the engine destructures from. The field existed on both
12072
+ * schemas and the engine still never read it, because nothing checks a
12073
+ * registered provider against `InferProvider<cap>` —
12074
+ * `ProviderRegistration.provider` is typed `object`.
12075
+ *
12076
+ * It is declared here anyway, and must stay in step with
12077
+ * `settings-store.query`: a caller reading only the cap definitions has
12078
+ * to be able to see that this call carries a projection.
12079
+ * `data-door-schema-parity.spec.ts` keeps the two aligned.
12080
+ */
12081
+ columns: array(string()).readonly().optional()
12082
+ }), array(SettingsRecordSchema).readonly(), { auth: "admin" }),
11968
12083
  /** Insert a new record. */
11969
12084
  insert: method(object({
11970
12085
  namespace: string().optional(),
11971
12086
  collection: string(),
11972
12087
  record: SettingsRecordSchema
11973
- }), _void(), { kind: "mutation" }),
12088
+ }), _void(), {
12089
+ kind: "mutation",
12090
+ auth: "admin"
12091
+ }),
11974
12092
  /** Update an existing record by ID. */
11975
12093
  update: method(object({
11976
12094
  namespace: string().optional(),
11977
12095
  collection: string(),
11978
12096
  id: string(),
11979
12097
  data: record(string(), unknown())
11980
- }), _void(), { kind: "mutation" }),
12098
+ }), _void(), {
12099
+ kind: "mutation",
12100
+ auth: "admin"
12101
+ }),
11981
12102
  /** Delete a record by key/ID. */
11982
12103
  delete: method(object({
11983
12104
  namespace: string().optional(),
11984
12105
  collection: string(),
11985
12106
  key: string()
11986
- }), _void(), { kind: "mutation" }),
12107
+ }), _void(), {
12108
+ kind: "mutation",
12109
+ auth: "admin"
12110
+ }),
11987
12111
  /** Delete every record matching `filter`, in one statement. */
11988
12112
  deleteWhere: method(object({
11989
12113
  namespace: string().optional(),
11990
12114
  collection: string(),
11991
12115
  filter: MutationFilterSchema
11992
- }), object({ deleted: number().int() }), { kind: "mutation" }),
12116
+ }), object({ deleted: number().int() }), {
12117
+ kind: "mutation",
12118
+ auth: "admin"
12119
+ }),
11993
12120
  /** Apply `data` to every record matching `filter`, in one statement. */
11994
12121
  updateWhere: method(object({
11995
12122
  namespace: string().optional(),
11996
12123
  collection: string(),
11997
12124
  filter: MutationFilterSchema,
11998
12125
  data: record(string(), unknown())
11999
- }), object({ updated: number().int() }), { kind: "mutation" }),
12126
+ }), object({ updated: number().int() }), {
12127
+ kind: "mutation",
12128
+ auth: "admin"
12129
+ }),
12000
12130
  /** Count entries in a collection, optionally filtered. */
12001
12131
  count: method(object({
12002
12132
  namespace: string().optional(),
12003
12133
  collection: string(),
12004
12134
  filter: QueryFilterSchema.optional()
12005
- }), number()),
12135
+ }), number(), { auth: "admin" }),
12006
12136
  /** Grouped counts per ((field-origin)/bucketSize) bucket, filtered. */
12007
12137
  histogram: method(object({
12008
12138
  namespace: string().optional(),
@@ -12014,19 +12144,22 @@ var dataStoreProviderCapability = {
12014
12144
  }), array(object({
12015
12145
  bucket: number().int(),
12016
12146
  count: number().int()
12017
- })).readonly()),
12147
+ })).readonly(), { auth: "admin" }),
12018
12148
  /** Check if a collection is empty. */
12019
12149
  isEmpty: method(object({
12020
12150
  namespace: string().optional(),
12021
12151
  collection: string()
12022
- }), boolean()),
12152
+ }), boolean(), { auth: "admin" }),
12023
12153
  /** Declare a typed (SQL-backed) collection with columns + indexes. */
12024
12154
  declareCollection: method(object({
12025
12155
  namespace: string().optional(),
12026
12156
  collection: string(),
12027
12157
  columns: array(CollectionColumnSchema).readonly(),
12028
12158
  indexes: array(CollectionIndexSchema).readonly().optional()
12029
- }), _void(), { kind: "mutation" })
12159
+ }), _void(), {
12160
+ kind: "mutation",
12161
+ auth: "admin"
12162
+ })
12030
12163
  }
12031
12164
  };
12032
12165
  /**
@@ -13826,13 +13959,20 @@ var embeddingEncoderCapability = {
13826
13959
  mode: "collection",
13827
13960
  internal: true,
13828
13961
  methods: {
13962
+ /**
13963
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
13964
+ * `data-store-provider`) — every method below was reachable on the
13965
+ * AppRouter by ANY authenticated session at the default
13966
+ * `auth: 'protected'`. Consumers reach the encoder via `ctx.api` from
13967
+ * `addon-post-analysis`, never tRPC.
13968
+ */
13829
13969
  encode: method(object({
13830
13970
  crop: _instanceof(Uint8Array),
13831
13971
  width: number(),
13832
13972
  height: number()
13833
- }), EmbeddingResultSchema),
13834
- encodeText: method(object({ text: string() }), EmbeddingResultSchema),
13835
- getInfo: method(_void(), EmbeddingInfoSchema)
13973
+ }), EmbeddingResultSchema, { auth: "admin" }),
13974
+ encodeText: method(object({ text: string() }), EmbeddingResultSchema, { auth: "admin" }),
13975
+ getInfo: method(_void(), EmbeddingInfoSchema, { auth: "admin" })
13836
13976
  }
13837
13977
  };
13838
13978
  /**
@@ -14141,12 +14281,28 @@ var llmRuntimeCapability = {
14141
14281
  mode: "singleton",
14142
14282
  internal: true,
14143
14283
  methods: {
14284
+ /**
14285
+ * `complete`, `status`, `listLocalModels` and `getDiskUsage` had NO
14286
+ * `auth` override — `internal: true` did not gate the mount (see the
14287
+ * 2026-08-26 note on `data-store-provider`), so they were reachable on
14288
+ * the AppRouter by ANY authenticated session at the default
14289
+ * `auth: 'protected'`, inconsistent with the rest of this cap (already
14290
+ * `auth: 'admin'`) and with this file's own docblock: "the operator
14291
+ * reaches this only through the `llm` cap's methods." The sole caller,
14292
+ * `addon-ai`'s `runtime-client.ts`, reaches every method here via
14293
+ * `ctx.api` + `nodePin` — never tRPC — so `auth: 'admin'` costs it
14294
+ * nothing; the public `llm` cap (which end users and non-admin sessions
14295
+ * legitimately call for chat/generation) is untouched.
14296
+ */
14144
14297
  complete: method(LlmGenerateBaseInputSchema.extend({
14145
14298
  images: array(LlmImageSchema).optional(),
14146
14299
  runtime: ManagedRuntimeConfigSchema,
14147
14300
  /** The managed profile's timeout, threaded by the hub provider. */
14148
14301
  timeoutMs: number().int().positive().optional()
14149
- }), LlmGenerateResultSchema, { kind: "mutation" }),
14302
+ }), LlmGenerateResultSchema, {
14303
+ kind: "mutation",
14304
+ auth: "admin"
14305
+ }),
14150
14306
  ensureStarted: method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
14151
14307
  kind: "mutation",
14152
14308
  auth: "admin"
@@ -14155,7 +14311,7 @@ var llmRuntimeCapability = {
14155
14311
  kind: "mutation",
14156
14312
  auth: "admin"
14157
14313
  }),
14158
- status: method(object({}), LlmRuntimeStatusSchema),
14314
+ status: method(object({}), LlmRuntimeStatusSchema, { auth: "admin" }),
14159
14315
  installModel: method(object({ model: ManagedModelRefSchema }), _void(), {
14160
14316
  kind: "mutation",
14161
14317
  auth: "admin"
@@ -14164,8 +14320,8 @@ var llmRuntimeCapability = {
14164
14320
  kind: "mutation",
14165
14321
  auth: "admin"
14166
14322
  }),
14167
- listLocalModels: method(object({}), array(LlmNodeModelSchema)),
14168
- getDiskUsage: method(object({}), LlmRuntimeDiskUsageSchema)
14323
+ listLocalModels: method(object({}), array(LlmNodeModelSchema), { auth: "admin" }),
14324
+ getDiskUsage: method(object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" })
14169
14325
  }
14170
14326
  };
14171
14327
  /**
@@ -18431,7 +18587,17 @@ var oauthIntegrationCapability = {
18431
18587
  scope: "system",
18432
18588
  mode: "collection",
18433
18589
  internal: true,
18434
- methods: { getDescriptor: method(_void(), OauthIntegrationDescriptorSchema) }
18590
+ methods: {
18591
+ /**
18592
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
18593
+ * `data-store-provider`) — `getDescriptor` was reachable on the AppRouter
18594
+ * by ANY authenticated session at the default `auth: 'protected'`. The
18595
+ * real caller is `/api/oauth2/authorize` and `/api/oauth2/integrations`
18596
+ * (`oauth2-routes.ts`), which resolve the provider directly off the
18597
+ * capability registry — never through tRPC. `auth: 'admin'` closes the
18598
+ * tRPC surface without touching that path.
18599
+ */
18600
+ getDescriptor: method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" }) }
18435
18601
  };
18436
18602
  /**
18437
18603
  * pipeline-analytics — device-scoped wrapper cap. Refines raw
@@ -19830,6 +19996,29 @@ var pipelineAnalyticsCapability = {
19830
19996
  kind: "mutation",
19831
19997
  auth: "admin"
19832
19998
  }),
19999
+ /**
20000
+ * Moves event media between locations — the OPERATOR's mover, without the
20001
+ * coordinator's lease. Twin of `recording.relocateFootage`: the same
20002
+ * engine the lease-gated coordinated migration uses
20003
+ * (`startStorageMigrationMove`), armable in the background and WITHOUT
20004
+ * pausing anything. Exists because `eventMedia` was the only storage
20005
+ * class whose only move path went through the recorder's global pause.
20006
+ */
20007
+ relocateMedia: method(RelocateMediaInputSchema, object({ jobId: string() }), {
20008
+ kind: "mutation",
20009
+ auth: "admin"
20010
+ }),
20011
+ /** Every relocate job this addon knows about, newest first (in RAM: the
20012
+ * move is resumable, so a lost list costs nothing but the display). */
20013
+ listRelocateMediaJobs: method(object({}), array(RelocateJobSchema).readonly(), {
20014
+ kind: "query",
20015
+ auth: "admin"
20016
+ }),
20017
+ /** Cancel a running or queued relocate job. */
20018
+ cancelRelocateMedia: method(object({ jobId: string() }), object({ cancelled: boolean() }), {
20019
+ kind: "mutation",
20020
+ auth: "admin"
20021
+ }),
19833
20022
  listOpsLog: method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19834
20023
  kind: "query",
19835
20024
  auth: "admin"
@@ -20189,6 +20378,11 @@ object({
20189
20378
  "jpeg"
20190
20379
  ])
20191
20380
  });
20381
+ /**
20382
+ * Process-local frame identity. It is serializable so it can ride an in-process
20383
+ * capability call, but `registryId` deliberately prevents resolution in any
20384
+ * other process or execution group.
20385
+ */
20192
20386
  var FrameRefSchema = object({
20193
20387
  registryId: string().min(1),
20194
20388
  id: string().min(1),
@@ -23142,6 +23336,20 @@ var ssoBridgeCapability = {
23142
23336
  mode: "singleton",
23143
23337
  internal: true,
23144
23338
  methods: {
23339
+ /**
23340
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
23341
+ * `data-store-provider`) — `signBridgeToken` was reachable on the
23342
+ * AppRouter by ANY authenticated session at the default
23343
+ * `auth: 'protected'`. The claims (including `isAdmin`) are CALLER-
23344
+ * SUPPLIED: an unprivileged session could have minted its own
23345
+ * `isAdmin: true` bridge token and redeemed it at
23346
+ * `/api/auth/sso/finish` to become admin — a full privilege escalation
23347
+ * through the exact surface this cap exists to close (see the module
23348
+ * docblock). Every real caller (`oauth-grants.ts`, `main.ts`
23349
+ * registration, the OIDC/magic-link addons) reaches this through
23350
+ * `ctx.api` / the in-process registry — never through tRPC — so
23351
+ * `auth: 'admin'` costs those callers nothing.
23352
+ */
23145
23353
  signBridgeToken: method(object({
23146
23354
  claims: SsoBridgeClaimsSchema,
23147
23355
  /**
@@ -23154,8 +23362,8 @@ var ssoBridgeCapability = {
23154
23362
  * linking rather than produce an eternal token.
23155
23363
  */
23156
23364
  ttlSec: union([number().int().positive(), literal("never")]).optional()
23157
- }), object({ token: string() })),
23158
- verifyBridgeToken: method(object({ token: string() }), SsoBridgeClaimsSchema.nullable())
23365
+ }), object({ token: string() }), { auth: "admin" }),
23366
+ verifyBridgeToken: method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" })
23159
23367
  }
23160
23368
  };
23161
23369
  var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
@@ -23304,8 +23512,15 @@ var storageEvictableCapability = {
23304
23512
  mode: "collection",
23305
23513
  internal: true,
23306
23514
  methods: {
23515
+ /**
23516
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
23517
+ * `data-store-provider`) — this was reachable on the AppRouter by ANY
23518
+ * authenticated session at the default `auth: 'protected'`. The only
23519
+ * consumer is the orchestrator's `StoragePressureManager`, which calls
23520
+ * `ctx.api` — never tRPC — so `auth: 'admin'` costs it nothing.
23521
+ */
23307
23522
  /** Bytes this provider holds on the given location — drives proportional fan-out. */
23308
- getEvictableUsage: method(object({ locationId: string() }), EvictableUsageSchema),
23523
+ getEvictableUsage: method(object({ locationId: string() }), EvictableUsageSchema, { auth: "admin" }),
23309
23524
  /**
23310
23525
  * Free approximately `targetBytes` of this provider's OWN least-valuable
23311
23526
  * data on the location (oldest footage, expired clips, …). Returns what it
@@ -23314,7 +23529,10 @@ var storageEvictableCapability = {
23314
23529
  evict: method(object({
23315
23530
  locationId: string(),
23316
23531
  targetBytes: number().int().positive()
23317
- }), EvictResultSchema, { kind: "mutation" })
23532
+ }), EvictResultSchema, {
23533
+ kind: "mutation",
23534
+ auth: "admin"
23535
+ })
23318
23536
  }
23319
23537
  };
23320
23538
  /**
@@ -23417,43 +23635,82 @@ var storageProviderCapability = {
23417
23635
  mode: "collection",
23418
23636
  internal: true,
23419
23637
  methods: {
23638
+ /**
23639
+ * Every method below except `testLocation` had NO `auth` override —
23640
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
23641
+ * `data-store-provider`), so this was reachable on the AppRouter by ANY
23642
+ * authenticated session at the default `auth: 'protected'`: arbitrary
23643
+ * read/write/delete/list against any storage location the operator has
23644
+ * configured, keyed only by a caller-supplied `location` + `relativePath`
23645
+ * — the same "public consumers never see this cap" claim that was false
23646
+ * for `data-store-provider`, here gating raw file I/O instead of raw
23647
+ * settings rows. The only legitimate caller,
23648
+ * `addon-registry.service.ts`'s `activeStorageProvider`, calls the
23649
+ * provider object directly (in-process) — never tRPC — so `auth: 'admin'`
23650
+ * on every method costs it nothing. Public consumers already go through
23651
+ * the separate `storage` singleton cap.
23652
+ */
23420
23653
  /** Self-description for the "Add location" wizard. */
23421
- getProviderInfo: method(_void(), ProviderInfoSchema),
23654
+ getProviderInfo: method(_void(), ProviderInfoSchema, { auth: "admin" }),
23422
23655
  /** Validate config + probe connectivity. Called on add and on edit. */
23423
23656
  testLocation: method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }),
23424
23657
  resolve: method(object({
23425
23658
  location: StorageLocationSchema,
23426
23659
  relativePath: string()
23427
- }), string()),
23660
+ }), string(), { auth: "admin" }),
23428
23661
  write: method(object({
23429
23662
  location: StorageLocationSchema,
23430
23663
  relativePath: string(),
23431
23664
  data: _instanceof(Uint8Array)
23432
- }), _void(), { kind: "mutation" }),
23665
+ }), _void(), {
23666
+ kind: "mutation",
23667
+ auth: "admin"
23668
+ }),
23433
23669
  read: method(object({
23434
23670
  location: StorageLocationSchema,
23435
23671
  relativePath: string()
23436
- }), _instanceof(Uint8Array)),
23672
+ }), _instanceof(Uint8Array), { auth: "admin" }),
23437
23673
  exists: method(object({
23438
23674
  location: StorageLocationSchema,
23439
23675
  relativePath: string()
23440
- }), boolean()),
23676
+ }), boolean(), { auth: "admin" }),
23441
23677
  list: method(object({
23442
23678
  location: StorageLocationSchema,
23443
23679
  prefix: string().optional()
23444
- }), array(string()).readonly()),
23680
+ }), array(string()).readonly(), { auth: "admin" }),
23445
23681
  delete: method(object({
23446
23682
  location: StorageLocationSchema,
23447
23683
  relativePath: string()
23448
- }), _void(), { kind: "mutation" }),
23449
- getAvailableSpace: method(object({ location: StorageLocationSchema }), number().nullable()),
23450
- beginUpload: method(BeginUploadInputSchema, BeginUploadResultSchema, { kind: "mutation" }),
23451
- writeChunk: method(WriteChunkInputSchema, _void(), { kind: "mutation" }),
23452
- finalizeUpload: method(FinalizeUploadInputSchema, _void(), { kind: "mutation" }),
23453
- abortUpload: method(AbortUploadInputSchema, _void(), { kind: "mutation" }),
23454
- beginDownload: method(BeginDownloadInputSchema, BeginDownloadResultSchema, { kind: "mutation" }),
23455
- readChunk: method(ReadChunkInputSchema, _instanceof(Uint8Array)),
23456
- endDownload: method(EndDownloadInputSchema, _void(), { kind: "mutation" })
23684
+ }), _void(), {
23685
+ kind: "mutation",
23686
+ auth: "admin"
23687
+ }),
23688
+ getAvailableSpace: method(object({ location: StorageLocationSchema }), number().nullable(), { auth: "admin" }),
23689
+ beginUpload: method(BeginUploadInputSchema, BeginUploadResultSchema, {
23690
+ kind: "mutation",
23691
+ auth: "admin"
23692
+ }),
23693
+ writeChunk: method(WriteChunkInputSchema, _void(), {
23694
+ kind: "mutation",
23695
+ auth: "admin"
23696
+ }),
23697
+ finalizeUpload: method(FinalizeUploadInputSchema, _void(), {
23698
+ kind: "mutation",
23699
+ auth: "admin"
23700
+ }),
23701
+ abortUpload: method(AbortUploadInputSchema, _void(), {
23702
+ kind: "mutation",
23703
+ auth: "admin"
23704
+ }),
23705
+ beginDownload: method(BeginDownloadInputSchema, BeginDownloadResultSchema, {
23706
+ kind: "mutation",
23707
+ auth: "admin"
23708
+ }),
23709
+ readChunk: method(ReadChunkInputSchema, _instanceof(Uint8Array), { auth: "admin" }),
23710
+ endDownload: method(EndDownloadInputSchema, _void(), {
23711
+ kind: "mutation",
23712
+ auth: "admin"
23713
+ })
23457
23714
  }
23458
23715
  };
23459
23716
  /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
@@ -23786,9 +24043,28 @@ var userPasskeysCapability = {
23786
24043
  auth: "admin",
23787
24044
  access: "create"
23788
24045
  }),
24046
+ /**
24047
+ * The four methods below had NO `auth` override — `internal: true` did
24048
+ * not gate the mount (see the 2026-08-26 note on `data-store-provider`),
24049
+ * so they were reachable on the AppRouter at the default
24050
+ * `auth: 'protected'` (any authenticated session), inconsistent with
24051
+ * every other method on this cap (all already `auth: 'admin'`).
24052
+ *
24053
+ * ⚠ Checked before gating: these are also the PRE-AUTH login-ceremony
24054
+ * methods, so admin-gating them here must not break login for an
24055
+ * unauthenticated principal. It doesn't — the real login path is the
24056
+ * PUBLIC `auth.beginAuthentication` / `auth.finishAuthentication` /
24057
+ * `auth.beginDiscoverableAuthentication` / `auth.finishDiscoverableAuthentication`
24058
+ * procedures in `server/backend/src/api/core/auth.router.ts`, which call
24059
+ * `provider.beginAuthentication(...)` etc. directly against the
24060
+ * capability-registry-resolved provider — never through this cap's own
24061
+ * tRPC route. `auth: 'admin'` here only closes a redundant, unused
24062
+ * second entry point onto the same ceremony.
24063
+ */
23789
24064
  beginAuthentication: method(object({ userId: string().optional() }), object({ optionsJSON: record(string(), unknown()) }), {
23790
24065
  kind: "mutation",
23791
- access: "view"
24066
+ access: "view",
24067
+ auth: "admin"
23792
24068
  }),
23793
24069
  finishAuthentication: method(object({
23794
24070
  /** Required — the user the assertion belongs to (verified). */
@@ -23797,11 +24073,13 @@ var userPasskeysCapability = {
23797
24073
  response: record(string(), unknown())
23798
24074
  }), object({ verified: boolean() }), {
23799
24075
  kind: "mutation",
23800
- access: "view"
24076
+ access: "view",
24077
+ auth: "admin"
23801
24078
  }),
23802
24079
  beginDiscoverableAuthentication: method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
23803
24080
  kind: "mutation",
23804
- access: "view"
24081
+ access: "view",
24082
+ auth: "admin"
23805
24083
  }),
23806
24084
  finishDiscoverableAuthentication: method(object({
23807
24085
  /** AuthenticationResponseJSON from the browser. */
@@ -23810,7 +24088,8 @@ response: record(string(), unknown()) }), object({
23810
24088
  userId: string().nullable()
23811
24089
  }), {
23812
24090
  kind: "mutation",
23813
- access: "view"
24091
+ access: "view",
24092
+ auth: "admin"
23814
24093
  }),
23815
24094
  listPasskeys: method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }),
23816
24095
  removePasskey: method(object({
@@ -23988,6 +24267,14 @@ var VectorStatsResultSchema = object({
23988
24267
  /** False when the backend ranks approximately. */
23989
24268
  exact: boolean()
23990
24269
  });
24270
+ /**
24271
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
24272
+ * `data-store-provider`) — every method below was reachable on the AppRouter
24273
+ * by ANY authenticated session at the default `auth: 'protected'`. The only
24274
+ * consumers (`addon-post-analysis`'s embedding stores) reach this cap via
24275
+ * `ctx.api` — UDS/Moleculer, never tRPC — so `auth: 'admin'` on every method
24276
+ * costs them nothing. No client (viewer or admin-ui) calls `vectorStore.*`.
24277
+ */
23991
24278
  var vectorStoreCapability = {
23992
24279
  name: "vector-store",
23993
24280
  scope: "system",
@@ -23995,14 +24282,26 @@ var vectorStoreCapability = {
23995
24282
  internal: true,
23996
24283
  methods: {
23997
24284
  /** Idempotent. Re-declaring with a different `dim` is an error, not a wipe. */
23998
- declareIndex: method(VectorDeclareIndexInputSchema, _void(), { kind: "mutation" }),
23999
- upsert: method(VectorUpsertInputSchema, VectorUpsertResultSchema, { kind: "mutation" }),
24000
- query: method(VectorQueryInputSchema, VectorQueryResultSchema),
24285
+ declareIndex: method(VectorDeclareIndexInputSchema, _void(), {
24286
+ kind: "mutation",
24287
+ auth: "admin"
24288
+ }),
24289
+ upsert: method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
24290
+ kind: "mutation",
24291
+ auth: "admin"
24292
+ }),
24293
+ query: method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }),
24001
24294
  /** Metadata by id, no vectors — see {@link VectorGetResultSchema}. */
24002
- getByIds: method(VectorGetInputSchema, VectorGetResultSchema),
24003
- deleteByIds: method(VectorDeleteInputSchema, VectorDeleteResultSchema, { kind: "mutation" }),
24004
- deleteByFilter: method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, { kind: "mutation" }),
24005
- stats: method(VectorStatsInputSchema, VectorStatsResultSchema)
24295
+ getByIds: method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }),
24296
+ deleteByIds: method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
24297
+ kind: "mutation",
24298
+ auth: "admin"
24299
+ }),
24300
+ deleteByFilter: method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
24301
+ kind: "mutation",
24302
+ auth: "admin"
24303
+ }),
24304
+ stats: method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" })
24006
24305
  }
24007
24306
  };
24008
24307
  var ClipSchema = object({
@@ -26851,7 +27150,27 @@ var faceGalleryCapability = {
26851
27150
  scope: "system",
26852
27151
  mode: "singleton",
26853
27152
  methods: {
26854
- listIdentities: method(_void(), array(IdentitySchema).readonly()),
27153
+ listIdentities: method(object({
27154
+ /**
27155
+ * Inline {@link IdentitySchema.coverBase64} on every row.
27156
+ *
27157
+ * Default `false`, the same inversion `listRecentFaces` and
27158
+ * `listPlates` took on 2026-08-25 (see `include-crops-default.ts` for
27159
+ * why the burden belongs on the caller that WANTS the bytes). Measured
27160
+ * on the live hub the same day: four identities cost 40,979 B with the
27161
+ * covers inline, ~10 KB of base64 per row, on a query this UI mounts
27162
+ * four times and the viewer holds at `staleTime: 30_000`.
27163
+ *
27164
+ * Nothing loses its avatar: `coverMediaKey` is already on every row and
27165
+ * the `event-media` plane serves that key `immutable` with an ETag.
27166
+ *
27167
+ * **This is an INPUT field, so it does not reach the addon until the
27168
+ * next train** — the hub router validates cap inputs against its own
27169
+ * compiled Zod and strips a key it does not know. Until then the
27170
+ * provider sees `undefined`, which resolves to `false`: the cheap shape
27171
+ * is what ships, and the opt-in becomes reachable when the train lands.
27172
+ */
27173
+ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly()),
26855
27174
  createIdentity: method(object({ name: string().min(1) }), IdentitySchema, {
26856
27175
  kind: "mutation",
26857
27176
  auth: "admin"
@@ -30104,8 +30423,10 @@ var PlateInfoSchema = object({
30104
30423
  keyFrameMediaKey: string().optional(),
30105
30424
  base64: string().optional(),
30106
30425
  /**
30107
- * Same crop as a data-plane URL. `getPlateByTrack` returns this and
30108
- * never inlines JPEG; `listPlates` still inlines for the admin-ui.
30426
+ * Same crop as a data-plane URL, always present when the plate has a stored
30427
+ * crop. `getPlateByTrack` returns this and never inlines JPEG; `listPlates`
30428
+ * and `searchPlates` inline the crop only when their `includeCrops` input is
30429
+ * left at its `true` default.
30109
30430
  */
30110
30431
  cropUrl: string().optional()
30111
30432
  });
@@ -30138,7 +30459,25 @@ var plateGalleryCapability = {
30138
30459
  methods: {
30139
30460
  listPlates: method(object({
30140
30461
  deviceId: number().int().optional(),
30141
- limit: number().int().positive().optional()
30462
+ limit: number().int().positive().optional(),
30463
+ /**
30464
+ * Inline the base64 crop on every row. Default `true` — the existing
30465
+ * behaviour, kept so no caller breaks.
30466
+ *
30467
+ * Set `false` once the caller renders {@link PlateInfo.cropUrl}.
30468
+ * Measured on the live hub at the 500 rows the Plates view asks for:
30469
+ * 879,403 B and 27.7 s with the crops inline, against a few KiB of
30470
+ * metadata without them — and the browser then caches the images.
30471
+ *
30472
+ * This is the plate twin of `faceGallery.listRecentFaces`'s option;
30473
+ * plates were the one gallery list left without it.
30474
+ *
30475
+ * **This is an INPUT field, so it does not reach the addon until the
30476
+ * next train.** The hub router validates cap inputs against its own
30477
+ * compiled Zod and strips a key it does not know. Until the train
30478
+ * ships, sending `false` is harmless and keeps the crops inline.
30479
+ */
30480
+ includeCrops: boolean().optional()
30142
30481
  }).optional(), array(PlateInfoSchema).readonly()),
30143
30482
  getPlateByTrack: method(object({
30144
30483
  deviceId: number().int(),
@@ -30149,7 +30488,9 @@ var plateGalleryCapability = {
30149
30488
  searchPlates: method(object({
30150
30489
  text: string().min(1),
30151
30490
  maxDistance: number().int().min(0).optional(),
30152
- limit: number().int().positive().optional()
30491
+ limit: number().int().positive().optional(),
30492
+ /** See `listPlates.includeCrops`. Default `true`. */
30493
+ includeCrops: boolean().optional()
30153
30494
  }), array(PlateInfoSchema).readonly()),
30154
30495
  /** Suggest groups of near-identical reads (same physical plate) for review/merge. */
30155
30496
  suggestPlateClusters: method(object({
@@ -30169,7 +30510,13 @@ var plateGalleryCapability = {
30169
30510
  kind: "mutation",
30170
30511
  auth: "admin"
30171
30512
  }),
30172
- listVehicles: method(_void(), array(VehicleSchema).readonly()),
30513
+ listVehicles: method(object({
30514
+ /** Inline {@link VehicleSchema.coverBase64} on every row. Default
30515
+ * `false` — the vehicle twin of `faceGallery.listIdentities`'s
30516
+ * option; `coverMediaKey` + the `event-media` plane carry the picture.
30517
+ * INPUT field: stripped by the hub router until the train ships, which
30518
+ * resolves to `false` and is exactly the intended default. */
30519
+ includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()),
30173
30520
  createVehicle: method(object({ name: string().min(1) }), VehicleSchema, {
30174
30521
  kind: "mutation",
30175
30522
  auth: "admin"
@@ -36913,6 +37260,12 @@ Object.freeze({
36913
37260
  addonId: null,
36914
37261
  access: "create"
36915
37262
  },
37263
+ "pipelineAnalytics.cancelRelocateMedia": {
37264
+ capName: "pipeline-analytics",
37265
+ capScope: "device",
37266
+ addonId: null,
37267
+ access: "create"
37268
+ },
36916
37269
  "pipelineAnalytics.cancelStorageMigrationMove": {
36917
37270
  capName: "pipeline-analytics",
36918
37271
  capScope: "device",
@@ -37087,6 +37440,12 @@ Object.freeze({
37087
37440
  addonId: null,
37088
37441
  access: "view"
37089
37442
  },
37443
+ "pipelineAnalytics.listRelocateMediaJobs": {
37444
+ capName: "pipeline-analytics",
37445
+ capScope: "device",
37446
+ addonId: null,
37447
+ access: "view"
37448
+ },
37090
37449
  "pipelineAnalytics.listRetrainAnnotations": {
37091
37450
  capName: "pipeline-analytics",
37092
37451
  capScope: "device",
@@ -37165,6 +37524,12 @@ Object.freeze({
37165
37524
  addonId: null,
37166
37525
  access: "create"
37167
37526
  },
37527
+ "pipelineAnalytics.relocateMedia": {
37528
+ capName: "pipeline-analytics",
37529
+ capScope: "device",
37530
+ addonId: null,
37531
+ access: "create"
37532
+ },
37168
37533
  "pipelineAnalytics.restageRetrainTrack": {
37169
37534
  capName: "pipeline-analytics",
37170
37535
  capScope: "device",