camstack 1.2.42 → 1.2.44

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.
@@ -14522,7 +14522,7 @@ function date4(params) {
14522
14522
  // ../../node_modules/zod/v4/classic/external.js
14523
14523
  config(en_default());
14524
14524
 
14525
- // ../types/dist/sleep-BLxqGLY0.mjs
14525
+ // ../types/dist/sleep-swxwnJmL.mjs
14526
14526
  var WELL_KNOWN_TABS = [
14527
14527
  {
14528
14528
  id: "overview",
@@ -15002,7 +15002,7 @@ function method(input, output, options) {
15002
15002
  input,
15003
15003
  output,
15004
15004
  kind: options?.kind ?? "query",
15005
- auth: options?.auth ?? "protected",
15005
+ ...options?.auth !== void 0 ? { auth: options.auth } : {},
15006
15006
  ...options?.access !== void 0 ? { access: options.access } : {},
15007
15007
  ...options?.caller !== void 0 ? { caller: options.caller } : {},
15008
15008
  timeoutMs: options?.timeoutMs
@@ -15025,8 +15025,17 @@ var adminUiCapability = {
15025
15025
  mode: "singleton",
15026
15026
  internal: true,
15027
15027
  methods: {
15028
- getStaticDir: method(external_exports.void(), StaticDirOutputSchema$1),
15029
- getVersion: method(external_exports.void(), VersionOutputSchema$1)
15028
+ /**
15029
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
15030
+ * `data-store-provider`) — both methods were reachable on the AppRouter
15031
+ * by ANY authenticated session at the default `auth: 'protected'`, and
15032
+ * `getStaticDir` leaks a hub filesystem path. The only caller is
15033
+ * `main.ts`'s static-file bootstrap, via `capRegistry.getSingletonForNode`
15034
+ * — never tRPC — so `auth: 'admin'` costs it nothing. The actual admin-ui
15035
+ * SPA assets are served as plain static files, unaffected by this gate.
15036
+ */
15037
+ getStaticDir: method(external_exports.void(), StaticDirOutputSchema$1, { auth: "admin" }),
15038
+ getVersion: method(external_exports.void(), VersionOutputSchema$1, { auth: "admin" })
15030
15039
  }
15031
15040
  };
15032
15041
  var DeviceType = /* @__PURE__ */ (function(DeviceType2) {
@@ -15175,8 +15184,23 @@ var viewerUiCapability = {
15175
15184
  mode: "singleton",
15176
15185
  internal: true,
15177
15186
  methods: {
15178
- getStaticDir: method(external_exports.void(), StaticDirOutputSchema),
15179
- getVersion: method(external_exports.void(), VersionOutputSchema)
15187
+ /**
15188
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
15189
+ * `data-store-provider`) — both methods were reachable on the AppRouter
15190
+ * by ANY authenticated session at the default `auth: 'protected'`, and
15191
+ * `getStaticDir` leaks a hub filesystem path.
15192
+ *
15193
+ * ⚠ Checked before gating: the VIEWER app IS used by a non-admin user
15194
+ * (`roberta`), but it never calls `viewerUi.*` over tRPC. The static SPA
15195
+ * it loads is served by the plain `GET /viewer/*` Fastify route in
15196
+ * `main.ts`, which reads `staticDir`/`indexPath` from a module-level
15197
+ * cache populated ONCE at boot via `capRegistry.getSingletonForNode`
15198
+ * (in-process, not tRPC) — the cap's tRPC procedure is never on that
15199
+ * request path. `auth: 'admin'` here does not block a single viewer
15200
+ * user from loading the app.
15201
+ */
15202
+ getStaticDir: method(external_exports.void(), StaticDirOutputSchema, { auth: "admin" }),
15203
+ getVersion: method(external_exports.void(), VersionOutputSchema, { auth: "admin" })
15180
15204
  }
15181
15205
  };
15182
15206
  var StreamSourceEntrySchema = external_exports.object({
@@ -15610,13 +15634,6 @@ var RecordingRetentionSchema = external_exports.object({
15610
15634
  maxAgeDays: external_exports.number().min(0).optional(),
15611
15635
  maxSizeGb: external_exports.number().min(0).optional()
15612
15636
  });
15613
- var ScrubThumbnailPresetSchema = external_exports.enum([
15614
- "minimal",
15615
- "low",
15616
- "standard",
15617
- "high",
15618
- "max"
15619
- ]);
15620
15637
  var RecordingConfigSchema = external_exports.object({
15621
15638
  enabled: external_exports.boolean(),
15622
15639
  /** DERIVED summary of `bands`, stamped by the recorder on every save.
@@ -15636,14 +15653,7 @@ var RecordingConfigSchema = external_exports.object({
15636
15653
  * "off" is the absence of a covering band, never a band value.
15637
15654
  */
15638
15655
  bands: external_exports.array(RecordingBandSchema).default([]),
15639
- retention: RecordingRetentionSchema.optional(),
15640
- /**
15641
- * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
15642
- * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
15643
- * windows only — existing sheets are immutable, and each window's index
15644
- * carries its own tile dims so mixed-preset history renders correctly.
15645
- */
15646
- scrubThumbnails: ScrubThumbnailPresetSchema.optional()
15656
+ retention: RecordingRetentionSchema.optional()
15647
15657
  }).strict();
15648
15658
  var RelocateJobStateSchema = external_exports.enum([
15649
15659
  "queued",
@@ -15932,7 +15942,7 @@ var CLASS_MAP_MACRO_TARGETS = [
15932
15942
  "animal",
15933
15943
  "package"
15934
15944
  ];
15935
- var ClassMapDefinitionSchema = external_exports.object({
15945
+ var DetectionCatalogClassMapSchema = external_exports.object({
15936
15946
  mapping: external_exports.record(external_exports.string(), external_exports.enum(CLASS_MAP_MACRO_TARGETS)),
15937
15947
  preserveOriginal: external_exports.boolean()
15938
15948
  });
@@ -16103,7 +16113,7 @@ var ModelCatalogEntrySchema = external_exports.object({
16103
16113
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
16104
16114
  * labels already ARE the CamStack macros (Scrypted identity map).
16105
16115
  */
16106
- classMap: ClassMapDefinitionSchema.optional()
16116
+ classMap: DetectionCatalogClassMapSchema.optional()
16107
16117
  });
16108
16118
  var ConvertTargetSchema = external_exports.discriminatedUnion("format", [external_exports.object({
16109
16119
  format: external_exports.literal("openvino"),
@@ -16133,7 +16143,7 @@ var ModelConvertMetadataSchema = external_exports.object({
16133
16143
  "segmentation"
16134
16144
  ]),
16135
16145
  faceAlignment: external_exports.boolean().optional(),
16136
- classMap: ClassMapDefinitionSchema.optional()
16146
+ classMap: DetectionCatalogClassMapSchema.optional()
16137
16147
  });
16138
16148
  var ConvertArtifactSchema = external_exports.object({
16139
16149
  format: external_exports.enum(MODEL_FORMATS),
@@ -16857,7 +16867,19 @@ var addonPagesSourceCapability = {
16857
16867
  scope: "system",
16858
16868
  mode: "collection",
16859
16869
  internal: true,
16860
- methods: { listPages: method(external_exports.void(), external_exports.array(AddonPageDeclarationSchema).readonly()) }
16870
+ methods: {
16871
+ /**
16872
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
16873
+ * `data-store-provider`) — this was reachable on the AppRouter by ANY
16874
+ * authenticated session at the default `auth: 'protected'`. The only
16875
+ * consumer, the `addon-pages-aggregator` builtin, reads it via
16876
+ * `ctx.capabilities.getCollection('addon-pages-source')` — LOCAL-PROCESS
16877
+ * only, never tRPC (per `ctx.capabilities` seeing only the local
16878
+ * process). The public, enriched listing admin-ui actually consumes is
16879
+ * the separate `addon-pages` cap, unaffected by this change.
16880
+ */
16881
+ listPages: method(external_exports.void(), external_exports.array(AddonPageDeclarationSchema).readonly(), { auth: "admin" })
16882
+ }
16861
16883
  };
16862
16884
  var AddonHttpRouteSchema = external_exports.object({
16863
16885
  method: external_exports.enum([
@@ -17061,7 +17083,21 @@ var addonWidgetsSourceCapability = {
17061
17083
  scope: "system",
17062
17084
  mode: "collection",
17063
17085
  internal: true,
17064
- methods: { listWidgets: method(external_exports.void(), external_exports.array(WidgetMetadataSchema).readonly()) }
17086
+ methods: {
17087
+ /**
17088
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
17089
+ * `data-store-provider`) — this was reachable on the AppRouter by ANY
17090
+ * authenticated session at the default `auth: 'protected'`. The only
17091
+ * consumer, the `addon-widgets-aggregator` builtin, reads it via
17092
+ * `ctx.capabilities.getCollection('addon-widgets-source')` — LOCAL-PROCESS
17093
+ * only, never tRPC. The public, enriched listing admin-ui actually
17094
+ * consumes is the separate `addon-widgets` cap (`listWidgets` there is
17095
+ * gated separately, and a `preAuth: true` widget is surfaced through the
17096
+ * PUBLIC `auth.listLoginMethods` contribution channel instead) —
17097
+ * neither is affected by this change.
17098
+ */
17099
+ listWidgets: method(external_exports.void(), external_exports.array(WidgetMetadataSchema).readonly(), { auth: "admin" })
17100
+ }
17065
17101
  };
17066
17102
  var EnrichedWidgetMetadataSchema = WidgetMetadataSchema.extend({
17067
17103
  addonId: external_exports.string(),
@@ -18941,7 +18977,18 @@ var customModelRegistryCapability = {
18941
18977
  scope: "system",
18942
18978
  mode: "collection",
18943
18979
  internal: true,
18944
- methods: { listModels: method(external_exports.void(), external_exports.array(CustomModelDescriptorSchema).readonly()) }
18980
+ methods: {
18981
+ /**
18982
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
18983
+ * `data-store-provider`) — this was reachable on the AppRouter by ANY
18984
+ * authenticated session at the default `auth: 'protected'`. The only
18985
+ * consumer, the detection-pipeline's model resolution
18986
+ * (`addon-pipeline/src/detection-pipeline/provider.ts`), calls
18987
+ * `ctx.api.customModelRegistry.listModels.query()` — UDS/Moleculer,
18988
+ * never tRPC — so `auth: 'admin'` costs it nothing.
18989
+ */
18990
+ listModels: method(external_exports.void(), external_exports.array(CustomModelDescriptorSchema).readonly(), { auth: "admin" })
18991
+ }
18945
18992
  };
18946
18993
  var QueryFilterSchema = external_exports.object({
18947
18994
  where: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
@@ -19013,11 +19060,26 @@ var settingsStoreCapability = {
19013
19060
  key: external_exports.string(),
19014
19061
  value: external_exports.unknown()
19015
19062
  }), external_exports.void(), { kind: "mutation" }),
19016
- /** Get all entries matching an optional filter. */
19063
+ /**
19064
+ * Rows matching a filter.
19065
+ *
19066
+ * `columns` is a SQL-level projection: the engine SELECTs only those
19067
+ * columns (plus the primary key) instead of the whole row. It is the only
19068
+ * way to stop paying disk for a column that is discarded — a heavy JSON
19069
+ * column dominates the row, and dropping it in JS after the read pays every
19070
+ * byte first. Measured on the live `pipeline-analytics:tracks` table: a
19071
+ * 501-row page is 4.6 MB whole and 0.11 MB projected, same index, same
19072
+ * plan.
19073
+ *
19074
+ * The engine REFUSES a column it cannot serve rather than omitting it —
19075
+ * see `SettingsQueryInput.columns` for why a projection may not be
19076
+ * forgiving the way a predicate is.
19077
+ */
19017
19078
  query: method(external_exports.object({
19018
19079
  namespace: external_exports.string().optional(),
19019
19080
  collection: external_exports.string(),
19020
- filter: QueryFilterSchema.optional()
19081
+ filter: QueryFilterSchema.optional(),
19082
+ columns: external_exports.array(external_exports.string()).readonly().optional()
19021
19083
  }), external_exports.array(SettingsRecordSchema).readonly()),
19022
19084
  /** Insert a new record. */
19023
19085
  insert: method(external_exports.object({
@@ -19133,64 +19195,105 @@ var dataStoreProviderCapability = {
19133
19195
  internal: true,
19134
19196
  methods: {
19135
19197
  /** Self-description — how the orchestrator picks a registrant. */
19136
- getEngineInfo: method(external_exports.void(), EngineInfoSchema),
19198
+ getEngineInfo: method(external_exports.void(), EngineInfoSchema, { auth: "admin" }),
19137
19199
  /** Get a single value by key from a collection. */
19138
19200
  get: method(external_exports.object({
19139
19201
  namespace: external_exports.string().optional(),
19140
19202
  collection: external_exports.string(),
19141
19203
  key: external_exports.string()
19142
- }), external_exports.unknown()),
19204
+ }), external_exports.unknown(), { auth: "admin" }),
19143
19205
  /** Set a value by key in a collection (upsert). */
19144
19206
  set: method(external_exports.object({
19145
19207
  namespace: external_exports.string().optional(),
19146
19208
  collection: external_exports.string(),
19147
19209
  key: external_exports.string(),
19148
19210
  value: external_exports.unknown()
19149
- }), external_exports.void(), { kind: "mutation" }),
19211
+ }), external_exports.void(), {
19212
+ kind: "mutation",
19213
+ auth: "admin"
19214
+ }),
19150
19215
  /** Get all entries matching an optional filter. */
19151
19216
  query: method(external_exports.object({
19152
19217
  namespace: external_exports.string().optional(),
19153
19218
  collection: external_exports.string(),
19154
- filter: QueryFilterSchema.optional()
19155
- }), external_exports.array(SettingsRecordSchema).readonly()),
19219
+ filter: QueryFilterSchema.optional(),
19220
+ /**
19221
+ * SQL-level column projection — MUST mirror `settings-store.query`.
19222
+ *
19223
+ * ⚠ An earlier version of this comment blamed Zod stripping, and that
19224
+ * was wrong — corrected 2026-08-26 after the hop map
19225
+ * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
19226
+ * There is **no Zod parse at all** between the door and the engine: the
19227
+ * dispatcher forwards the payload verbatim and UDS carries it whole. A
19228
+ * field declared here reaches `SqliteSettingsBackend` either way.
19229
+ *
19230
+ * What actually lost `columns` was the THIRD declaration of this shape:
19231
+ * `SettingsQueryInput` in `interfaces/storage.ts`, a hand-written TS
19232
+ * interface the engine destructures from. The field existed on both
19233
+ * schemas and the engine still never read it, because nothing checks a
19234
+ * registered provider against `InferProvider<cap>` —
19235
+ * `ProviderRegistration.provider` is typed `object`.
19236
+ *
19237
+ * It is declared here anyway, and must stay in step with
19238
+ * `settings-store.query`: a caller reading only the cap definitions has
19239
+ * to be able to see that this call carries a projection.
19240
+ * `data-door-schema-parity.spec.ts` keeps the two aligned.
19241
+ */
19242
+ columns: external_exports.array(external_exports.string()).readonly().optional()
19243
+ }), external_exports.array(SettingsRecordSchema).readonly(), { auth: "admin" }),
19156
19244
  /** Insert a new record. */
19157
19245
  insert: method(external_exports.object({
19158
19246
  namespace: external_exports.string().optional(),
19159
19247
  collection: external_exports.string(),
19160
19248
  record: SettingsRecordSchema
19161
- }), external_exports.void(), { kind: "mutation" }),
19249
+ }), external_exports.void(), {
19250
+ kind: "mutation",
19251
+ auth: "admin"
19252
+ }),
19162
19253
  /** Update an existing record by ID. */
19163
19254
  update: method(external_exports.object({
19164
19255
  namespace: external_exports.string().optional(),
19165
19256
  collection: external_exports.string(),
19166
19257
  id: external_exports.string(),
19167
19258
  data: external_exports.record(external_exports.string(), external_exports.unknown())
19168
- }), external_exports.void(), { kind: "mutation" }),
19259
+ }), external_exports.void(), {
19260
+ kind: "mutation",
19261
+ auth: "admin"
19262
+ }),
19169
19263
  /** Delete a record by key/ID. */
19170
19264
  delete: method(external_exports.object({
19171
19265
  namespace: external_exports.string().optional(),
19172
19266
  collection: external_exports.string(),
19173
19267
  key: external_exports.string()
19174
- }), external_exports.void(), { kind: "mutation" }),
19268
+ }), external_exports.void(), {
19269
+ kind: "mutation",
19270
+ auth: "admin"
19271
+ }),
19175
19272
  /** Delete every record matching `filter`, in one statement. */
19176
19273
  deleteWhere: method(external_exports.object({
19177
19274
  namespace: external_exports.string().optional(),
19178
19275
  collection: external_exports.string(),
19179
19276
  filter: MutationFilterSchema
19180
- }), external_exports.object({ deleted: external_exports.number().int() }), { kind: "mutation" }),
19277
+ }), external_exports.object({ deleted: external_exports.number().int() }), {
19278
+ kind: "mutation",
19279
+ auth: "admin"
19280
+ }),
19181
19281
  /** Apply `data` to every record matching `filter`, in one statement. */
19182
19282
  updateWhere: method(external_exports.object({
19183
19283
  namespace: external_exports.string().optional(),
19184
19284
  collection: external_exports.string(),
19185
19285
  filter: MutationFilterSchema,
19186
19286
  data: external_exports.record(external_exports.string(), external_exports.unknown())
19187
- }), external_exports.object({ updated: external_exports.number().int() }), { kind: "mutation" }),
19287
+ }), external_exports.object({ updated: external_exports.number().int() }), {
19288
+ kind: "mutation",
19289
+ auth: "admin"
19290
+ }),
19188
19291
  /** Count entries in a collection, optionally filtered. */
19189
19292
  count: method(external_exports.object({
19190
19293
  namespace: external_exports.string().optional(),
19191
19294
  collection: external_exports.string(),
19192
19295
  filter: QueryFilterSchema.optional()
19193
- }), external_exports.number()),
19296
+ }), external_exports.number(), { auth: "admin" }),
19194
19297
  /** Grouped counts per ((field-origin)/bucketSize) bucket, filtered. */
19195
19298
  histogram: method(external_exports.object({
19196
19299
  namespace: external_exports.string().optional(),
@@ -19202,19 +19305,22 @@ var dataStoreProviderCapability = {
19202
19305
  }), external_exports.array(external_exports.object({
19203
19306
  bucket: external_exports.number().int(),
19204
19307
  count: external_exports.number().int()
19205
- })).readonly()),
19308
+ })).readonly(), { auth: "admin" }),
19206
19309
  /** Check if a collection is empty. */
19207
19310
  isEmpty: method(external_exports.object({
19208
19311
  namespace: external_exports.string().optional(),
19209
19312
  collection: external_exports.string()
19210
- }), external_exports.boolean()),
19313
+ }), external_exports.boolean(), { auth: "admin" }),
19211
19314
  /** Declare a typed (SQL-backed) collection with columns + indexes. */
19212
19315
  declareCollection: method(external_exports.object({
19213
19316
  namespace: external_exports.string().optional(),
19214
19317
  collection: external_exports.string(),
19215
19318
  columns: external_exports.array(CollectionColumnSchema).readonly(),
19216
19319
  indexes: external_exports.array(CollectionIndexSchema).readonly().optional()
19217
- }), external_exports.void(), { kind: "mutation" })
19320
+ }), external_exports.void(), {
19321
+ kind: "mutation",
19322
+ auth: "admin"
19323
+ })
19218
19324
  }
19219
19325
  };
19220
19326
  var ShmRingStatsSchema = external_exports.object({
@@ -20756,13 +20862,20 @@ var embeddingEncoderCapability = {
20756
20862
  mode: "collection",
20757
20863
  internal: true,
20758
20864
  methods: {
20865
+ /**
20866
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
20867
+ * `data-store-provider`) — every method below was reachable on the
20868
+ * AppRouter by ANY authenticated session at the default
20869
+ * `auth: 'protected'`. Consumers reach the encoder via `ctx.api` from
20870
+ * `addon-post-analysis`, never tRPC.
20871
+ */
20759
20872
  encode: method(external_exports.object({
20760
20873
  crop: external_exports.instanceof(Uint8Array),
20761
20874
  width: external_exports.number(),
20762
20875
  height: external_exports.number()
20763
- }), EmbeddingResultSchema),
20764
- encodeText: method(external_exports.object({ text: external_exports.string() }), EmbeddingResultSchema),
20765
- getInfo: method(external_exports.void(), EmbeddingInfoSchema)
20876
+ }), EmbeddingResultSchema, { auth: "admin" }),
20877
+ encodeText: method(external_exports.object({ text: external_exports.string() }), EmbeddingResultSchema, { auth: "admin" }),
20878
+ getInfo: method(external_exports.void(), EmbeddingInfoSchema, { auth: "admin" })
20766
20879
  }
20767
20880
  };
20768
20881
  var DirEntrySchema = external_exports.object({
@@ -21028,7 +21141,23 @@ var llmRuntimeCapability = {
21028
21141
  mode: "singleton",
21029
21142
  internal: true,
21030
21143
  methods: {
21031
- complete: method(LlmRuntimeCompleteInputSchema, LlmGenerateResultSchema, { kind: "mutation" }),
21144
+ /**
21145
+ * `complete`, `status`, `listLocalModels` and `getDiskUsage` had NO
21146
+ * `auth` override — `internal: true` did not gate the mount (see the
21147
+ * 2026-08-26 note on `data-store-provider`), so they were reachable on
21148
+ * the AppRouter by ANY authenticated session at the default
21149
+ * `auth: 'protected'`, inconsistent with the rest of this cap (already
21150
+ * `auth: 'admin'`) and with this file's own docblock: "the operator
21151
+ * reaches this only through the `llm` cap's methods." The sole caller,
21152
+ * `addon-ai`'s `runtime-client.ts`, reaches every method here via
21153
+ * `ctx.api` + `nodePin` — never tRPC — so `auth: 'admin'` costs it
21154
+ * nothing; the public `llm` cap (which end users and non-admin sessions
21155
+ * legitimately call for chat/generation) is untouched.
21156
+ */
21157
+ complete: method(LlmRuntimeCompleteInputSchema, LlmGenerateResultSchema, {
21158
+ kind: "mutation",
21159
+ auth: "admin"
21160
+ }),
21032
21161
  ensureStarted: method(external_exports.object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
21033
21162
  kind: "mutation",
21034
21163
  auth: "admin"
@@ -21037,7 +21166,7 @@ var llmRuntimeCapability = {
21037
21166
  kind: "mutation",
21038
21167
  auth: "admin"
21039
21168
  }),
21040
- status: method(external_exports.object({}), LlmRuntimeStatusSchema),
21169
+ status: method(external_exports.object({}), LlmRuntimeStatusSchema, { auth: "admin" }),
21041
21170
  installModel: method(external_exports.object({ model: ManagedModelRefSchema }), external_exports.void(), {
21042
21171
  kind: "mutation",
21043
21172
  auth: "admin"
@@ -21046,8 +21175,8 @@ var llmRuntimeCapability = {
21046
21175
  kind: "mutation",
21047
21176
  auth: "admin"
21048
21177
  }),
21049
- listLocalModels: method(external_exports.object({}), external_exports.array(LlmNodeModelSchema)),
21050
- getDiskUsage: method(external_exports.object({}), LlmRuntimeDiskUsageSchema)
21178
+ listLocalModels: method(external_exports.object({}), external_exports.array(LlmNodeModelSchema), { auth: "admin" }),
21179
+ getDiskUsage: method(external_exports.object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" })
21051
21180
  }
21052
21181
  };
21053
21182
  var LlmProfileKindSchema = external_exports.enum([
@@ -24037,7 +24166,18 @@ var oauthIntegrationCapability = {
24037
24166
  scope: "system",
24038
24167
  mode: "collection",
24039
24168
  internal: true,
24040
- methods: { getDescriptor: method(external_exports.void(), OauthIntegrationDescriptorSchema) }
24169
+ methods: {
24170
+ /**
24171
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
24172
+ * `data-store-provider`) — `getDescriptor` was reachable on the AppRouter
24173
+ * by ANY authenticated session at the default `auth: 'protected'`. The
24174
+ * real caller is `/api/oauth2/authorize` and `/api/oauth2/integrations`
24175
+ * (`oauth2-routes.ts`), which resolve the provider directly off the
24176
+ * capability registry — never through tRPC. `auth: 'admin'` closes the
24177
+ * tRPC surface without touching that path.
24178
+ */
24179
+ getDescriptor: method(external_exports.void(), OauthIntegrationDescriptorSchema, { auth: "admin" })
24180
+ }
24041
24181
  };
24042
24182
  var TrackStateSchema = external_exports.enum([
24043
24183
  "new",
@@ -25167,6 +25307,29 @@ var pipelineAnalyticsCapability = {
25167
25307
  kind: "mutation",
25168
25308
  auth: "admin"
25169
25309
  }),
25310
+ /**
25311
+ * Moves event media between locations — the OPERATOR's mover, without the
25312
+ * coordinator's lease. Twin of `recording.relocateFootage`: the same
25313
+ * engine the lease-gated coordinated migration uses
25314
+ * (`startStorageMigrationMove`), armable in the background and WITHOUT
25315
+ * pausing anything. Exists because `eventMedia` was the only storage
25316
+ * class whose only move path went through the recorder's global pause.
25317
+ */
25318
+ relocateMedia: method(RelocateMediaInputSchema, external_exports.object({ jobId: external_exports.string() }), {
25319
+ kind: "mutation",
25320
+ auth: "admin"
25321
+ }),
25322
+ /** Every relocate job this addon knows about, newest first (in RAM: the
25323
+ * move is resumable, so a lost list costs nothing but the display). */
25324
+ listRelocateMediaJobs: method(external_exports.object({}), external_exports.array(RelocateJobSchema).readonly(), {
25325
+ kind: "query",
25326
+ auth: "admin"
25327
+ }),
25328
+ /** Cancel a running or queued relocate job. */
25329
+ cancelRelocateMedia: method(external_exports.object({ jobId: external_exports.string() }), external_exports.object({ cancelled: external_exports.boolean() }), {
25330
+ kind: "mutation",
25331
+ auth: "admin"
25332
+ }),
25170
25333
  listOpsLog: method(OpsLogQueryInputSchema, external_exports.array(OpsLogEntrySchema).readonly(), {
25171
25334
  kind: "query",
25172
25335
  auth: "admin"
@@ -28229,6 +28392,20 @@ var ssoBridgeCapability = {
28229
28392
  mode: "singleton",
28230
28393
  internal: true,
28231
28394
  methods: {
28395
+ /**
28396
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
28397
+ * `data-store-provider`) — `signBridgeToken` was reachable on the
28398
+ * AppRouter by ANY authenticated session at the default
28399
+ * `auth: 'protected'`. The claims (including `isAdmin`) are CALLER-
28400
+ * SUPPLIED: an unprivileged session could have minted its own
28401
+ * `isAdmin: true` bridge token and redeemed it at
28402
+ * `/api/auth/sso/finish` to become admin — a full privilege escalation
28403
+ * through the exact surface this cap exists to close (see the module
28404
+ * docblock). Every real caller (`oauth-grants.ts`, `main.ts`
28405
+ * registration, the OIDC/magic-link addons) reaches this through
28406
+ * `ctx.api` / the in-process registry — never through tRPC — so
28407
+ * `auth: 'admin'` costs those callers nothing.
28408
+ */
28232
28409
  signBridgeToken: method(external_exports.object({
28233
28410
  claims: SsoBridgeClaimsSchema,
28234
28411
  /**
@@ -28241,8 +28418,8 @@ var ssoBridgeCapability = {
28241
28418
  * linking rather than produce an eternal token.
28242
28419
  */
28243
28420
  ttlSec: external_exports.union([external_exports.number().int().positive(), external_exports.literal("never")]).optional()
28244
- }), external_exports.object({ token: external_exports.string() })),
28245
- verifyBridgeToken: method(external_exports.object({ token: external_exports.string() }), SsoBridgeClaimsSchema.nullable())
28421
+ }), external_exports.object({ token: external_exports.string() }), { auth: "admin" }),
28422
+ verifyBridgeToken: method(external_exports.object({ token: external_exports.string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" })
28246
28423
  }
28247
28424
  };
28248
28425
  var ProviderListEntrySchema = external_exports.discriminatedUnion("shouldSaveDiskSpace", [external_exports.object({
@@ -28360,8 +28537,15 @@ var storageEvictableCapability = {
28360
28537
  mode: "collection",
28361
28538
  internal: true,
28362
28539
  methods: {
28540
+ /**
28541
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
28542
+ * `data-store-provider`) — this was reachable on the AppRouter by ANY
28543
+ * authenticated session at the default `auth: 'protected'`. The only
28544
+ * consumer is the orchestrator's `StoragePressureManager`, which calls
28545
+ * `ctx.api` — never tRPC — so `auth: 'admin'` costs it nothing.
28546
+ */
28363
28547
  /** Bytes this provider holds on the given location — drives proportional fan-out. */
28364
- getEvictableUsage: method(external_exports.object({ locationId: external_exports.string() }), EvictableUsageSchema),
28548
+ getEvictableUsage: method(external_exports.object({ locationId: external_exports.string() }), EvictableUsageSchema, { auth: "admin" }),
28365
28549
  /**
28366
28550
  * Free approximately `targetBytes` of this provider's OWN least-valuable
28367
28551
  * data on the location (oldest footage, expired clips, …). Returns what it
@@ -28370,7 +28554,10 @@ var storageEvictableCapability = {
28370
28554
  evict: method(external_exports.object({
28371
28555
  locationId: external_exports.string(),
28372
28556
  targetBytes: external_exports.number().int().positive()
28373
- }), EvictResultSchema, { kind: "mutation" })
28557
+ }), EvictResultSchema, {
28558
+ kind: "mutation",
28559
+ auth: "admin"
28560
+ })
28374
28561
  }
28375
28562
  };
28376
28563
  var storageMigrationCapability = {
@@ -28448,43 +28635,82 @@ var storageProviderCapability = {
28448
28635
  mode: "collection",
28449
28636
  internal: true,
28450
28637
  methods: {
28638
+ /**
28639
+ * Every method below except `testLocation` had NO `auth` override —
28640
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
28641
+ * `data-store-provider`), so this was reachable on the AppRouter by ANY
28642
+ * authenticated session at the default `auth: 'protected'`: arbitrary
28643
+ * read/write/delete/list against any storage location the operator has
28644
+ * configured, keyed only by a caller-supplied `location` + `relativePath`
28645
+ * — the same "public consumers never see this cap" claim that was false
28646
+ * for `data-store-provider`, here gating raw file I/O instead of raw
28647
+ * settings rows. The only legitimate caller,
28648
+ * `addon-registry.service.ts`'s `activeStorageProvider`, calls the
28649
+ * provider object directly (in-process) — never tRPC — so `auth: 'admin'`
28650
+ * on every method costs it nothing. Public consumers already go through
28651
+ * the separate `storage` singleton cap.
28652
+ */
28451
28653
  /** Self-description for the "Add location" wizard. */
28452
- getProviderInfo: method(external_exports.void(), ProviderInfoSchema),
28654
+ getProviderInfo: method(external_exports.void(), ProviderInfoSchema, { auth: "admin" }),
28453
28655
  /** Validate config + probe connectivity. Called on add and on edit. */
28454
28656
  testLocation: method(external_exports.object({ config: external_exports.record(external_exports.string(), external_exports.unknown()) }), TestLocationResultSchema, { auth: "admin" }),
28455
28657
  resolve: method(external_exports.object({
28456
28658
  location: StorageLocationSchema,
28457
28659
  relativePath: external_exports.string()
28458
- }), external_exports.string()),
28660
+ }), external_exports.string(), { auth: "admin" }),
28459
28661
  write: method(external_exports.object({
28460
28662
  location: StorageLocationSchema,
28461
28663
  relativePath: external_exports.string(),
28462
28664
  data: external_exports.instanceof(Uint8Array)
28463
- }), external_exports.void(), { kind: "mutation" }),
28665
+ }), external_exports.void(), {
28666
+ kind: "mutation",
28667
+ auth: "admin"
28668
+ }),
28464
28669
  read: method(external_exports.object({
28465
28670
  location: StorageLocationSchema,
28466
28671
  relativePath: external_exports.string()
28467
- }), external_exports.instanceof(Uint8Array)),
28672
+ }), external_exports.instanceof(Uint8Array), { auth: "admin" }),
28468
28673
  exists: method(external_exports.object({
28469
28674
  location: StorageLocationSchema,
28470
28675
  relativePath: external_exports.string()
28471
- }), external_exports.boolean()),
28676
+ }), external_exports.boolean(), { auth: "admin" }),
28472
28677
  list: method(external_exports.object({
28473
28678
  location: StorageLocationSchema,
28474
28679
  prefix: external_exports.string().optional()
28475
- }), external_exports.array(external_exports.string()).readonly()),
28680
+ }), external_exports.array(external_exports.string()).readonly(), { auth: "admin" }),
28476
28681
  delete: method(external_exports.object({
28477
28682
  location: StorageLocationSchema,
28478
28683
  relativePath: external_exports.string()
28479
- }), external_exports.void(), { kind: "mutation" }),
28480
- getAvailableSpace: method(external_exports.object({ location: StorageLocationSchema }), external_exports.number().nullable()),
28481
- beginUpload: method(BeginUploadInputSchema, BeginUploadResultSchema, { kind: "mutation" }),
28482
- writeChunk: method(WriteChunkInputSchema, external_exports.void(), { kind: "mutation" }),
28483
- finalizeUpload: method(FinalizeUploadInputSchema, external_exports.void(), { kind: "mutation" }),
28484
- abortUpload: method(AbortUploadInputSchema, external_exports.void(), { kind: "mutation" }),
28485
- beginDownload: method(BeginDownloadInputSchema, BeginDownloadResultSchema, { kind: "mutation" }),
28486
- readChunk: method(ReadChunkInputSchema, external_exports.instanceof(Uint8Array)),
28487
- endDownload: method(EndDownloadInputSchema, external_exports.void(), { kind: "mutation" })
28684
+ }), external_exports.void(), {
28685
+ kind: "mutation",
28686
+ auth: "admin"
28687
+ }),
28688
+ getAvailableSpace: method(external_exports.object({ location: StorageLocationSchema }), external_exports.number().nullable(), { auth: "admin" }),
28689
+ beginUpload: method(BeginUploadInputSchema, BeginUploadResultSchema, {
28690
+ kind: "mutation",
28691
+ auth: "admin"
28692
+ }),
28693
+ writeChunk: method(WriteChunkInputSchema, external_exports.void(), {
28694
+ kind: "mutation",
28695
+ auth: "admin"
28696
+ }),
28697
+ finalizeUpload: method(FinalizeUploadInputSchema, external_exports.void(), {
28698
+ kind: "mutation",
28699
+ auth: "admin"
28700
+ }),
28701
+ abortUpload: method(AbortUploadInputSchema, external_exports.void(), {
28702
+ kind: "mutation",
28703
+ auth: "admin"
28704
+ }),
28705
+ beginDownload: method(BeginDownloadInputSchema, BeginDownloadResultSchema, {
28706
+ kind: "mutation",
28707
+ auth: "admin"
28708
+ }),
28709
+ readChunk: method(ReadChunkInputSchema, external_exports.instanceof(Uint8Array), { auth: "admin" }),
28710
+ endDownload: method(EndDownloadInputSchema, external_exports.void(), {
28711
+ kind: "mutation",
28712
+ auth: "admin"
28713
+ })
28488
28714
  }
28489
28715
  };
28490
28716
  var ProfileSettingsSchemaBridge = external_exports.unknown().nullable();
@@ -28723,9 +28949,28 @@ var userPasskeysCapability = {
28723
28949
  auth: "admin",
28724
28950
  access: "create"
28725
28951
  }),
28952
+ /**
28953
+ * The four methods below had NO `auth` override — `internal: true` did
28954
+ * not gate the mount (see the 2026-08-26 note on `data-store-provider`),
28955
+ * so they were reachable on the AppRouter at the default
28956
+ * `auth: 'protected'` (any authenticated session), inconsistent with
28957
+ * every other method on this cap (all already `auth: 'admin'`).
28958
+ *
28959
+ * ⚠ Checked before gating: these are also the PRE-AUTH login-ceremony
28960
+ * methods, so admin-gating them here must not break login for an
28961
+ * unauthenticated principal. It doesn't — the real login path is the
28962
+ * PUBLIC `auth.beginAuthentication` / `auth.finishAuthentication` /
28963
+ * `auth.beginDiscoverableAuthentication` / `auth.finishDiscoverableAuthentication`
28964
+ * procedures in `server/backend/src/api/core/auth.router.ts`, which call
28965
+ * `provider.beginAuthentication(...)` etc. directly against the
28966
+ * capability-registry-resolved provider — never through this cap's own
28967
+ * tRPC route. `auth: 'admin'` here only closes a redundant, unused
28968
+ * second entry point onto the same ceremony.
28969
+ */
28726
28970
  beginAuthentication: method(external_exports.object({ userId: external_exports.string().optional() }), external_exports.object({ optionsJSON: external_exports.record(external_exports.string(), external_exports.unknown()) }), {
28727
28971
  kind: "mutation",
28728
- access: "view"
28972
+ access: "view",
28973
+ auth: "admin"
28729
28974
  }),
28730
28975
  finishAuthentication: method(external_exports.object({
28731
28976
  /** Required — the user the assertion belongs to (verified). */
@@ -28734,11 +28979,13 @@ var userPasskeysCapability = {
28734
28979
  response: external_exports.record(external_exports.string(), external_exports.unknown())
28735
28980
  }), external_exports.object({ verified: external_exports.boolean() }), {
28736
28981
  kind: "mutation",
28737
- access: "view"
28982
+ access: "view",
28983
+ auth: "admin"
28738
28984
  }),
28739
28985
  beginDiscoverableAuthentication: method(external_exports.object({}), external_exports.object({ optionsJSON: external_exports.record(external_exports.string(), external_exports.unknown()) }), {
28740
28986
  kind: "mutation",
28741
- access: "view"
28987
+ access: "view",
28988
+ auth: "admin"
28742
28989
  }),
28743
28990
  finishDiscoverableAuthentication: method(external_exports.object({
28744
28991
  /** AuthenticationResponseJSON from the browser. */
@@ -28748,7 +28995,8 @@ var userPasskeysCapability = {
28748
28995
  userId: external_exports.string().nullable()
28749
28996
  }), {
28750
28997
  kind: "mutation",
28751
- access: "view"
28998
+ access: "view",
28999
+ auth: "admin"
28752
29000
  }),
28753
29001
  listPasskeys: method(external_exports.object({ userId: external_exports.string() }), external_exports.array(PasskeySummarySchema), { auth: "admin" }),
28754
29002
  removePasskey: method(external_exports.object({
@@ -28873,14 +29121,26 @@ var vectorStoreCapability = {
28873
29121
  internal: true,
28874
29122
  methods: {
28875
29123
  /** Idempotent. Re-declaring with a different `dim` is an error, not a wipe. */
28876
- declareIndex: method(VectorDeclareIndexInputSchema, external_exports.void(), { kind: "mutation" }),
28877
- upsert: method(VectorUpsertInputSchema, VectorUpsertResultSchema, { kind: "mutation" }),
28878
- query: method(VectorQueryInputSchema, VectorQueryResultSchema),
29124
+ declareIndex: method(VectorDeclareIndexInputSchema, external_exports.void(), {
29125
+ kind: "mutation",
29126
+ auth: "admin"
29127
+ }),
29128
+ upsert: method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
29129
+ kind: "mutation",
29130
+ auth: "admin"
29131
+ }),
29132
+ query: method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }),
28879
29133
  /** Metadata by id, no vectors — see {@link VectorGetResultSchema}. */
28880
- getByIds: method(VectorGetInputSchema, VectorGetResultSchema),
28881
- deleteByIds: method(VectorDeleteInputSchema, VectorDeleteResultSchema, { kind: "mutation" }),
28882
- deleteByFilter: method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, { kind: "mutation" }),
28883
- stats: method(VectorStatsInputSchema, VectorStatsResultSchema)
29134
+ getByIds: method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }),
29135
+ deleteByIds: method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
29136
+ kind: "mutation",
29137
+ auth: "admin"
29138
+ }),
29139
+ deleteByFilter: method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
29140
+ kind: "mutation",
29141
+ auth: "admin"
29142
+ }),
29143
+ stats: method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" })
28884
29144
  }
28885
29145
  };
28886
29146
  var ClipSchema = external_exports.object({
@@ -31301,7 +31561,28 @@ var faceGalleryCapability = {
31301
31561
  scope: "system",
31302
31562
  mode: "singleton",
31303
31563
  methods: {
31304
- listIdentities: method(external_exports.void(), external_exports.array(IdentitySchema).readonly()),
31564
+ listIdentities: method(external_exports.object({
31565
+ /**
31566
+ * Inline {@link IdentitySchema.coverBase64} on every row.
31567
+ *
31568
+ * Default `false`, the same inversion `listRecentFaces` and
31569
+ * `listPlates` took on 2026-08-25 (see `include-crops-default.ts` for
31570
+ * why the burden belongs on the caller that WANTS the bytes). Measured
31571
+ * on the live hub the same day: four identities cost 40,979 B with the
31572
+ * covers inline, ~10 KB of base64 per row, on a query this UI mounts
31573
+ * four times and the viewer holds at `staleTime: 30_000`.
31574
+ *
31575
+ * Nothing loses its avatar: `coverMediaKey` is already on every row and
31576
+ * the `event-media` plane serves that key `immutable` with an ETag.
31577
+ *
31578
+ * **This is an INPUT field, so it does not reach the addon until the
31579
+ * next train** — the hub router validates cap inputs against its own
31580
+ * compiled Zod and strips a key it does not know. Until then the
31581
+ * provider sees `undefined`, which resolves to `false`: the cheap shape
31582
+ * is what ships, and the opt-in becomes reachable when the train lands.
31583
+ */
31584
+ includeCrops: external_exports.boolean().optional()
31585
+ }).optional(), external_exports.array(IdentitySchema).readonly()),
31305
31586
  createIdentity: method(external_exports.object({ name: external_exports.string().min(1) }), IdentitySchema, {
31306
31587
  kind: "mutation",
31307
31588
  auth: "admin"
@@ -34053,8 +34334,10 @@ var PlateInfoSchema = external_exports.object({
34053
34334
  keyFrameMediaKey: external_exports.string().optional(),
34054
34335
  base64: external_exports.string().optional(),
34055
34336
  /**
34056
- * Same crop as a data-plane URL. `getPlateByTrack` returns this and
34057
- * never inlines JPEG; `listPlates` still inlines for the admin-ui.
34337
+ * Same crop as a data-plane URL, always present when the plate has a stored
34338
+ * crop. `getPlateByTrack` returns this and never inlines JPEG; `listPlates`
34339
+ * and `searchPlates` inline the crop only when their `includeCrops` input is
34340
+ * left at its `true` default.
34058
34341
  */
34059
34342
  cropUrl: external_exports.string().optional()
34060
34343
  });
@@ -34079,7 +34362,25 @@ var plateGalleryCapability = {
34079
34362
  methods: {
34080
34363
  listPlates: method(external_exports.object({
34081
34364
  deviceId: external_exports.number().int().optional(),
34082
- limit: external_exports.number().int().positive().optional()
34365
+ limit: external_exports.number().int().positive().optional(),
34366
+ /**
34367
+ * Inline the base64 crop on every row. Default `true` — the existing
34368
+ * behaviour, kept so no caller breaks.
34369
+ *
34370
+ * Set `false` once the caller renders {@link PlateInfo.cropUrl}.
34371
+ * Measured on the live hub at the 500 rows the Plates view asks for:
34372
+ * 879,403 B and 27.7 s with the crops inline, against a few KiB of
34373
+ * metadata without them — and the browser then caches the images.
34374
+ *
34375
+ * This is the plate twin of `faceGallery.listRecentFaces`'s option;
34376
+ * plates were the one gallery list left without it.
34377
+ *
34378
+ * **This is an INPUT field, so it does not reach the addon until the
34379
+ * next train.** The hub router validates cap inputs against its own
34380
+ * compiled Zod and strips a key it does not know. Until the train
34381
+ * ships, sending `false` is harmless and keeps the crops inline.
34382
+ */
34383
+ includeCrops: external_exports.boolean().optional()
34083
34384
  }).optional(), external_exports.array(PlateInfoSchema).readonly()),
34084
34385
  getPlateByTrack: method(external_exports.object({
34085
34386
  deviceId: external_exports.number().int(),
@@ -34090,7 +34391,9 @@ var plateGalleryCapability = {
34090
34391
  searchPlates: method(external_exports.object({
34091
34392
  text: external_exports.string().min(1),
34092
34393
  maxDistance: external_exports.number().int().min(0).optional(),
34093
- limit: external_exports.number().int().positive().optional()
34394
+ limit: external_exports.number().int().positive().optional(),
34395
+ /** See `listPlates.includeCrops`. Default `true`. */
34396
+ includeCrops: external_exports.boolean().optional()
34094
34397
  }), external_exports.array(PlateInfoSchema).readonly()),
34095
34398
  /** Suggest groups of near-identical reads (same physical plate) for review/merge. */
34096
34399
  suggestPlateClusters: method(external_exports.object({
@@ -34110,7 +34413,14 @@ var plateGalleryCapability = {
34110
34413
  kind: "mutation",
34111
34414
  auth: "admin"
34112
34415
  }),
34113
- listVehicles: method(external_exports.void(), external_exports.array(VehicleSchema).readonly()),
34416
+ listVehicles: method(external_exports.object({
34417
+ /** Inline {@link VehicleSchema.coverBase64} on every row. Default
34418
+ * `false` — the vehicle twin of `faceGallery.listIdentities`'s
34419
+ * option; `coverMediaKey` + the `event-media` plane carry the picture.
34420
+ * INPUT field: stripped by the hub router until the train ships, which
34421
+ * resolves to `false` and is exactly the intended default. */
34422
+ includeCrops: external_exports.boolean().optional()
34423
+ }).optional(), external_exports.array(VehicleSchema).readonly()),
34114
34424
  createVehicle: method(external_exports.object({ name: external_exports.string().min(1) }), VehicleSchema, {
34115
34425
  kind: "mutation",
34116
34426
  auth: "admin"
@@ -40031,6 +40341,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
40031
40341
  addonId: null,
40032
40342
  access: "create"
40033
40343
  },
40344
+ "pipelineAnalytics.cancelRelocateMedia": {
40345
+ capName: "pipeline-analytics",
40346
+ capScope: "device",
40347
+ addonId: null,
40348
+ access: "create"
40349
+ },
40034
40350
  "pipelineAnalytics.cancelStorageMigrationMove": {
40035
40351
  capName: "pipeline-analytics",
40036
40352
  capScope: "device",
@@ -40205,6 +40521,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
40205
40521
  addonId: null,
40206
40522
  access: "view"
40207
40523
  },
40524
+ "pipelineAnalytics.listRelocateMediaJobs": {
40525
+ capName: "pipeline-analytics",
40526
+ capScope: "device",
40527
+ addonId: null,
40528
+ access: "view"
40529
+ },
40208
40530
  "pipelineAnalytics.listRetrainAnnotations": {
40209
40531
  capName: "pipeline-analytics",
40210
40532
  capScope: "device",
@@ -40283,6 +40605,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
40283
40605
  addonId: null,
40284
40606
  access: "create"
40285
40607
  },
40608
+ "pipelineAnalytics.relocateMedia": {
40609
+ capName: "pipeline-analytics",
40610
+ capScope: "device",
40611
+ addonId: null,
40612
+ access: "create"
40613
+ },
40286
40614
  "pipelineAnalytics.restageRetrainTrack": {
40287
40615
  capName: "pipeline-analytics",
40288
40616
  capScope: "device",