@camstack/addon-terminal 0.1.32 → 0.1.34

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 (3) hide show
  1. package/dist/addon.js +435 -112
  2. package/dist/addon.mjs +435 -112
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -5833,6 +5833,13 @@ var BaseAddon = class {
5833
5833
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5834
5834
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5835
5835
  _registeredCapNames = [];
5836
+ /**
5837
+ * True only after `readAddonStore` actually answered. Constructor
5838
+ * defaults look like stored config when the store is down — a forked
5839
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5840
+ * mode, 2026-08-25) is not "the operator chose this".
5841
+ */
5842
+ settingsStoreReady = false;
5836
5843
  /** Default config values. Provided via constructor. */
5837
5844
  defaults;
5838
5845
  constructor(defaults) {
@@ -6233,7 +6240,9 @@ var BaseAddon = class {
6233
6240
  ];
6234
6241
  let lastErr;
6235
6242
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6236
- return await settings.readAddonStore() ?? {};
6243
+ const stored = await settings.readAddonStore() ?? {};
6244
+ this.settingsStoreReady = true;
6245
+ return stored;
6237
6246
  } catch (err) {
6238
6247
  lastErr = err;
6239
6248
  const msg = err instanceof Error ? err.message : String(err);
@@ -6241,6 +6250,7 @@ var BaseAddon = class {
6241
6250
  if (attempt === delaysMs.length) break;
6242
6251
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6243
6252
  }
6253
+ this.settingsStoreReady = false;
6244
6254
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6245
6255
  return {};
6246
6256
  }
@@ -6711,7 +6721,7 @@ function method(input, output, options) {
6711
6721
  input,
6712
6722
  output,
6713
6723
  kind: options?.kind ?? "query",
6714
- auth: options?.auth ?? "protected",
6724
+ ...options?.auth !== void 0 ? { auth: options.auth } : {},
6715
6725
  ...options?.access !== void 0 ? { access: options.access } : {},
6716
6726
  ...options?.caller !== void 0 ? { caller: options.caller } : {},
6717
6727
  timeoutMs: options?.timeoutMs
@@ -6735,7 +6745,7 @@ function event(data) {
6735
6745
  }
6736
6746
  var StaticDirOutputSchema$1 = object({ staticDir: string() });
6737
6747
  var VersionOutputSchema$1 = object({ version: string() });
6738
- method(_void(), StaticDirOutputSchema$1), method(_void(), VersionOutputSchema$1);
6748
+ method(_void(), StaticDirOutputSchema$1, { auth: "admin" }), method(_void(), VersionOutputSchema$1, { auth: "admin" });
6739
6749
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
6740
6750
  DeviceType["Camera"] = "camera";
6741
6751
  DeviceType["Hub"] = "hub";
@@ -7056,7 +7066,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
7056
7066
  }({});
7057
7067
  var StaticDirOutputSchema = object({ staticDir: string() });
7058
7068
  var VersionOutputSchema = object({ version: string() });
7059
- method(_void(), StaticDirOutputSchema), method(_void(), VersionOutputSchema);
7069
+ method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
7060
7070
  /**
7061
7071
  * device-ops — device-scoped cap that unifies the per-IDevice operations
7062
7072
  * previously routed through the `.device-ops` Moleculer bridge service.
@@ -7690,24 +7700,6 @@ var RecordingRetentionSchema = object({
7690
7700
  maxSizeGb: number().min(0).optional()
7691
7701
  });
7692
7702
  /**
7693
- * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7694
- * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7695
- * previews at. Five graduated steps; absent on a config = `standard` (the
7696
- * shipped default, matching `sheet-geometry`/`sheet-composer`).
7697
- *
7698
- * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7699
- * Each window's index sidecar carries its own tile dims, so a camera whose
7700
- * preset changed over time renders every historical window at the dims it was
7701
- * written with.
7702
- */
7703
- var ScrubThumbnailPresetSchema = _enum([
7704
- "minimal",
7705
- "low",
7706
- "standard",
7707
- "high",
7708
- "max"
7709
- ]);
7710
- /**
7711
7703
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7712
7704
  *
7713
7705
  * `bands` is the ONLY authored recording intent: what to record, when, and on
@@ -7715,7 +7707,11 @@ var ScrubThumbnailPresetSchema = _enum([
7715
7707
  * other field is a storage knob (profiles, segment length, retention, scrub).
7716
7708
  *
7717
7709
  * STRICT on purpose: the legacy authoring surface (`schedule`/`schedules`/
7718
- * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30.
7710
+ * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30,
7711
+ * and `scrubThumbnails` — a five-step fidelity knob for a sprite tier that was
7712
+ * deleted on 2026-07-24 and had ZERO consumers in the recorder — on 2026-08-25
7713
+ * (D62: a switch that writes a store nobody reads is worse than no switch).
7714
+ * Stored rows keep loading: the READ schema is `.strip()` (config-store.ts).
7719
7715
  * A stale caller must fail loudly — silently stripping its legacy intent would
7720
7716
  * persist a band-less config, i.e. silently stop recording the camera.
7721
7717
  */
@@ -7738,14 +7734,7 @@ var RecordingConfigSchema = object({
7738
7734
  * "off" is the absence of a covering band, never a band value.
7739
7735
  */
7740
7736
  bands: array(RecordingBandSchema).default([]),
7741
- retention: RecordingRetentionSchema.optional(),
7742
- /**
7743
- * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7744
- * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7745
- * windows only — existing sheets are immutable, and each window's index
7746
- * carries its own tile dims so mixed-preset history renders correctly.
7747
- */
7748
- scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7737
+ retention: RecordingRetentionSchema.optional()
7749
7738
  }).strict();
7750
7739
  /**
7751
7740
  * Entity-relocation job state (storage entity-routing spec, Phase 4).
@@ -7821,10 +7810,11 @@ var RelocateFootageInputSchema = object({
7821
7810
  * `RecordingConfig.enabled` or camera wrapper bindings. */
7822
7811
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
7823
7812
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
7824
- var StorageMigrationMediaMoveInputSchema = object({
7813
+ var RelocateMediaInputSchema = object({
7825
7814
  toLocationId: string(),
7826
7815
  throttleMbps: number().min(1).max(1e3).optional()
7827
- }).extend({ leaseId: string().min(1) });
7816
+ });
7817
+ var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
7828
7818
  /** The independently selectable logical storage classes. `recordings`
7829
7819
  * encompasses the high and mid segment profiles; `recordingsLow` is low
7830
7820
  * segments; `eventMedia` is post-analysis blobs. */
@@ -8119,7 +8109,26 @@ var LabelDefinitionSchema = object({
8119
8109
  description: string().optional(),
8120
8110
  icon: string().optional()
8121
8111
  });
8122
- var ClassMapDefinitionSchema = object({
8112
+ /**
8113
+ * Wire schema for a per-model CATALOG classMap override
8114
+ * (`ModelCatalogEntry.classMap` / `ModelConvertMetadata.classMap`) —
8115
+ * restricted to {@link CLASS_MAP_MACRO_TARGETS}, the only macros the
8116
+ * detection pipeline executor actually routes.
8117
+ *
8118
+ * This is deliberately a DIFFERENT, narrower shape than the general-purpose
8119
+ * `ClassMapDefinition` interface above (e.g. `IDetectionAddon.getClassMap()`
8120
+ * and the audio `YAMNET_TO_MACRO` catalog both use macro targets outside this
8121
+ * enum) — the two used to share the name `ClassMapDefinition`/
8122
+ * `ClassMapDefinitionSchema`, which made the schema-type-twin guard
8123
+ * (`scripts/check-schema-type-twins.ts`) flag them as a duplicated shape. They
8124
+ * are not: it is two different concepts colliding on a name. Keep this type
8125
+ * under its own name rather than reusing `ClassMapDefinition` — reusing it
8126
+ * would either narrow every `ClassMapDefinition` consumer to the four
8127
+ * detection macros (breaking `YAMNET_TO_MACRO`) or drop the validation this
8128
+ * schema exists for (see the "rejects a classMap whose target is not a
8129
+ * detection macro" test in `model-catalog-schema.test.ts`).
8130
+ */
8131
+ var DetectionCatalogClassMapSchema = object({
8123
8132
  mapping: record(string(), _enum([
8124
8133
  "person",
8125
8134
  "vehicle",
@@ -8211,6 +8220,12 @@ var ModelVariantGroupSchema = object({
8211
8220
  */
8212
8221
  resolution: number().int().positive().optional()
8213
8222
  });
8223
+ var ModelProviderIdSchema = _enum([
8224
+ "camstack",
8225
+ "frigate",
8226
+ "scrypted",
8227
+ "custom"
8228
+ ]);
8214
8229
  var ModelCatalogEntrySchema = object({
8215
8230
  id: string(),
8216
8231
  name: string(),
@@ -8308,11 +8323,17 @@ var ModelCatalogEntrySchema = object({
8308
8323
  */
8309
8324
  group: ModelVariantGroupSchema.optional(),
8310
8325
  /**
8326
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8327
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8328
+ * persisted before this field existed (`inferModelProvider` fills those).
8329
+ */
8330
+ provider: ModelProviderIdSchema.optional(),
8331
+ /**
8311
8332
  * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8312
8333
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8313
8334
  * labels already ARE the CamStack macros (Scrypted identity map).
8314
8335
  */
8315
- classMap: ClassMapDefinitionSchema.optional()
8336
+ classMap: DetectionCatalogClassMapSchema.optional()
8316
8337
  });
8317
8338
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8318
8339
  format: literal("openvino"),
@@ -8342,7 +8363,7 @@ var ModelConvertMetadataSchema = object({
8342
8363
  "segmentation"
8343
8364
  ]),
8344
8365
  faceAlignment: boolean().optional(),
8345
- classMap: ClassMapDefinitionSchema.optional()
8366
+ classMap: DetectionCatalogClassMapSchema.optional()
8346
8367
  });
8347
8368
  var ConvertResultSchema = object({
8348
8369
  entry: ModelCatalogEntrySchema,
@@ -9205,7 +9226,7 @@ var AddonPageDeclarationSchema = object({
9205
9226
  /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
9206
9227
  sectionLabel: string().optional()
9207
9228
  });
9208
- method(_void(), array(AddonPageDeclarationSchema).readonly());
9229
+ method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
9209
9230
  var AddonHttpRouteSchema = object({
9210
9231
  method: _enum([
9211
9232
  "GET",
@@ -9440,7 +9461,7 @@ var WidgetMetadataSchema = object({
9440
9461
  defaultColumns: number().int().min(1).max(12).default(6),
9441
9462
  defaultRows: number().int().min(1).max(12).default(1)
9442
9463
  });
9443
- method(_void(), array(WidgetMetadataSchema).readonly());
9464
+ method(_void(), array(WidgetMetadataSchema).readonly(), { auth: "admin" });
9444
9465
  /**
9445
9466
  * `addon-widgets` — system-scoped singleton aggregator cap. Public-facing
9446
9467
  * surface that admin-ui consumes through `useAddonWidgetsListWidgets()`.
@@ -11064,7 +11085,7 @@ var CustomModelDescriptorSchema = object({
11064
11085
  stepId: string(),
11065
11086
  entry: ModelCatalogEntrySchema
11066
11087
  });
11067
- method(_void(), array(CustomModelDescriptorSchema).readonly());
11088
+ method(_void(), array(CustomModelDescriptorSchema).readonly(), { auth: "admin" });
11068
11089
  /**
11069
11090
  * Query filter for settings-store collections.
11070
11091
  */
@@ -11151,7 +11172,8 @@ method(object({
11151
11172
  }), _void(), { kind: "mutation" }), method(object({
11152
11173
  namespace: string().optional(),
11153
11174
  collection: string(),
11154
- filter: QueryFilterSchema.optional()
11175
+ filter: QueryFilterSchema.optional(),
11176
+ columns: array(string()).readonly().optional()
11155
11177
  }), array(SettingsRecordSchema).readonly()), method(object({
11156
11178
  namespace: string().optional(),
11157
11179
  collection: string(),
@@ -11214,46 +11236,87 @@ var EngineInfoSchema = object({
11214
11236
  kind: _enum(["relational", "vector"]),
11215
11237
  displayName: string()
11216
11238
  });
11217
- method(_void(), EngineInfoSchema), method(object({
11239
+ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
11218
11240
  namespace: string().optional(),
11219
11241
  collection: string(),
11220
11242
  key: string()
11221
- }), unknown()), method(object({
11243
+ }), unknown(), { auth: "admin" }), method(object({
11222
11244
  namespace: string().optional(),
11223
11245
  collection: string(),
11224
11246
  key: string(),
11225
11247
  value: unknown()
11226
- }), _void(), { kind: "mutation" }), method(object({
11248
+ }), _void(), {
11249
+ kind: "mutation",
11250
+ auth: "admin"
11251
+ }), method(object({
11227
11252
  namespace: string().optional(),
11228
11253
  collection: string(),
11229
- filter: QueryFilterSchema.optional()
11230
- }), array(SettingsRecordSchema).readonly()), method(object({
11254
+ filter: QueryFilterSchema.optional(),
11255
+ /**
11256
+ * SQL-level column projection — MUST mirror `settings-store.query`.
11257
+ *
11258
+ * ⚠ An earlier version of this comment blamed Zod stripping, and that
11259
+ * was wrong — corrected 2026-08-26 after the hop map
11260
+ * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
11261
+ * There is **no Zod parse at all** between the door and the engine: the
11262
+ * dispatcher forwards the payload verbatim and UDS carries it whole. A
11263
+ * field declared here reaches `SqliteSettingsBackend` either way.
11264
+ *
11265
+ * What actually lost `columns` was the THIRD declaration of this shape:
11266
+ * `SettingsQueryInput` in `interfaces/storage.ts`, a hand-written TS
11267
+ * interface the engine destructures from. The field existed on both
11268
+ * schemas and the engine still never read it, because nothing checks a
11269
+ * registered provider against `InferProvider<cap>` —
11270
+ * `ProviderRegistration.provider` is typed `object`.
11271
+ *
11272
+ * It is declared here anyway, and must stay in step with
11273
+ * `settings-store.query`: a caller reading only the cap definitions has
11274
+ * to be able to see that this call carries a projection.
11275
+ * `data-door-schema-parity.spec.ts` keeps the two aligned.
11276
+ */
11277
+ columns: array(string()).readonly().optional()
11278
+ }), array(SettingsRecordSchema).readonly(), { auth: "admin" }), method(object({
11231
11279
  namespace: string().optional(),
11232
11280
  collection: string(),
11233
11281
  record: SettingsRecordSchema
11234
- }), _void(), { kind: "mutation" }), method(object({
11282
+ }), _void(), {
11283
+ kind: "mutation",
11284
+ auth: "admin"
11285
+ }), method(object({
11235
11286
  namespace: string().optional(),
11236
11287
  collection: string(),
11237
11288
  id: string(),
11238
11289
  data: record(string(), unknown())
11239
- }), _void(), { kind: "mutation" }), method(object({
11290
+ }), _void(), {
11291
+ kind: "mutation",
11292
+ auth: "admin"
11293
+ }), method(object({
11240
11294
  namespace: string().optional(),
11241
11295
  collection: string(),
11242
11296
  key: string()
11243
- }), _void(), { kind: "mutation" }), method(object({
11297
+ }), _void(), {
11298
+ kind: "mutation",
11299
+ auth: "admin"
11300
+ }), method(object({
11244
11301
  namespace: string().optional(),
11245
11302
  collection: string(),
11246
11303
  filter: MutationFilterSchema
11247
- }), object({ deleted: number().int() }), { kind: "mutation" }), method(object({
11304
+ }), object({ deleted: number().int() }), {
11305
+ kind: "mutation",
11306
+ auth: "admin"
11307
+ }), method(object({
11248
11308
  namespace: string().optional(),
11249
11309
  collection: string(),
11250
11310
  filter: MutationFilterSchema,
11251
11311
  data: record(string(), unknown())
11252
- }), object({ updated: number().int() }), { kind: "mutation" }), method(object({
11312
+ }), object({ updated: number().int() }), {
11313
+ kind: "mutation",
11314
+ auth: "admin"
11315
+ }), method(object({
11253
11316
  namespace: string().optional(),
11254
11317
  collection: string(),
11255
11318
  filter: QueryFilterSchema.optional()
11256
- }), number()), method(object({
11319
+ }), number(), { auth: "admin" }), method(object({
11257
11320
  namespace: string().optional(),
11258
11321
  collection: string(),
11259
11322
  field: string(),
@@ -11263,15 +11326,18 @@ method(_void(), EngineInfoSchema), method(object({
11263
11326
  }), array(object({
11264
11327
  bucket: number().int(),
11265
11328
  count: number().int()
11266
- })).readonly()), method(object({
11329
+ })).readonly(), { auth: "admin" }), method(object({
11267
11330
  namespace: string().optional(),
11268
11331
  collection: string()
11269
- }), boolean()), method(object({
11332
+ }), boolean(), { auth: "admin" }), method(object({
11270
11333
  namespace: string().optional(),
11271
11334
  collection: string(),
11272
11335
  columns: array(CollectionColumnSchema).readonly(),
11273
11336
  indexes: array(CollectionIndexSchema).readonly().optional()
11274
- }), _void(), { kind: "mutation" });
11337
+ }), _void(), {
11338
+ kind: "mutation",
11339
+ auth: "admin"
11340
+ });
11275
11341
  /**
11276
11342
  * shm ring usage stats for a `frameSink: 'shm'` decoder session —
11277
11343
  * exposed via `decoder.getShmStats` so downstream consumers can
@@ -12044,6 +12110,27 @@ var LinkedDeviceSchema = object({
12044
12110
  features: array(string()),
12045
12111
  producesTrackedEvents: boolean().optional()
12046
12112
  });
12113
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
12114
+ * The batch answer needs the tag; the single-device answer already has it
12115
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
12116
+ var LinkedDevicesForDeviceSchema = object({
12117
+ deviceId: number(),
12118
+ mode: LinkedDevicesModeSchema,
12119
+ devices: array(LinkedDeviceSchema)
12120
+ });
12121
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
12122
+ * `getAllBindings` all answer in. Declared once: three copies of the same
12123
+ * object literal is exactly how the three drift apart. */
12124
+ var DeviceBindingsForDeviceSchema = object({
12125
+ deviceId: number(),
12126
+ entries: array(object({
12127
+ capName: string(),
12128
+ kind: _enum(["native", "wrapped"]),
12129
+ providerAddonId: string(),
12130
+ providerNodeId: string(),
12131
+ nativeAddonId: string()
12132
+ }))
12133
+ });
12047
12134
  var SavedDeviceRowSchema = object({
12048
12135
  /** Numeric id reserved at allocateDeviceId time. */
12049
12136
  id: number(),
@@ -12269,11 +12356,25 @@ method(object({
12269
12356
  projection: _enum(["full", "slim"]).optional(),
12270
12357
  /** Return only camera devices. Filtering server-side instead of
12271
12358
  * shipping 293 rows to find 12. */
12272
- isCamera: boolean().optional()
12359
+ isCamera: boolean().optional(),
12360
+ /**
12361
+ * Return only these device ids. For the caller that already KNOWS the
12362
+ * handful it wants and needs a field the id-bearing answer does not
12363
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12364
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12365
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12366
+ * refetches on the reconcile interval, on a phone.
12367
+ *
12368
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12369
+ * keys rather than rejecting them (verified against the live hub
12370
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12371
+ * it answers today and the caller filters as it already does.
12372
+ */
12373
+ deviceIds: array(number()).optional()
12273
12374
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
12274
12375
  mode: LinkedDevicesModeSchema,
12275
12376
  devices: array(LinkedDeviceSchema)
12276
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12377
+ })), method(object({ deviceIds: array(number()) }), array(LinkedDevicesForDeviceSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12277
12378
  deviceId: number(),
12278
12379
  values: record(string(), unknown())
12279
12380
  }), object({ success: literal(true) }), {
@@ -12300,25 +12401,7 @@ method(object({
12300
12401
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
12301
12402
  kind: "mutation",
12302
12403
  auth: "admin"
12303
- }), method(object({ deviceId: number() }), object({
12304
- deviceId: number(),
12305
- entries: array(object({
12306
- capName: string(),
12307
- kind: _enum(["native", "wrapped"]),
12308
- providerAddonId: string(),
12309
- providerNodeId: string(),
12310
- nativeAddonId: string()
12311
- }))
12312
- })), method(object({}), array(object({
12313
- deviceId: number(),
12314
- entries: array(object({
12315
- capName: string(),
12316
- kind: _enum(["native", "wrapped"]),
12317
- providerAddonId: string(),
12318
- providerNodeId: string(),
12319
- nativeAddonId: string()
12320
- }))
12321
- }))), method(object({
12404
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12322
12405
  deviceId: number(),
12323
12406
  capName: string(),
12324
12407
  wrapperAddonId: string(),
@@ -12489,7 +12572,7 @@ method(object({
12489
12572
  crop: _instanceof(Uint8Array),
12490
12573
  width: number(),
12491
12574
  height: number()
12492
- }), EmbeddingResultSchema), method(object({ text: string() }), EmbeddingResultSchema), method(_void(), EmbeddingInfoSchema);
12575
+ }), EmbeddingResultSchema, { auth: "admin" }), method(object({ text: string() }), EmbeddingResultSchema, { auth: "admin" }), method(_void(), EmbeddingInfoSchema, { auth: "admin" });
12493
12576
  /**
12494
12577
  * filesystem-browse — per-node capability for browsing the node's local
12495
12578
  * filesystem. Reads are unconfined (whole filesystem, from `/` down); WRITES
@@ -12782,19 +12865,22 @@ method(LlmGenerateBaseInputSchema.extend({
12782
12865
  runtime: ManagedRuntimeConfigSchema,
12783
12866
  /** The managed profile's timeout, threaded by the hub provider. */
12784
12867
  timeoutMs: number().int().positive().optional()
12785
- }), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
12868
+ }), LlmGenerateResultSchema, {
12869
+ kind: "mutation",
12870
+ auth: "admin"
12871
+ }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
12786
12872
  kind: "mutation",
12787
12873
  auth: "admin"
12788
12874
  }), method(object({}), _void(), {
12789
12875
  kind: "mutation",
12790
12876
  auth: "admin"
12791
- }), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
12877
+ }), method(object({}), LlmRuntimeStatusSchema, { auth: "admin" }), method(object({ model: ManagedModelRefSchema }), _void(), {
12792
12878
  kind: "mutation",
12793
12879
  auth: "admin"
12794
12880
  }), method(object({ file: string() }), _void(), {
12795
12881
  kind: "mutation",
12796
12882
  auth: "admin"
12797
- }), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
12883
+ }), method(object({}), array(LlmNodeModelSchema), { auth: "admin" }), method(object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" });
12798
12884
  /**
12799
12885
  * `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
12800
12886
  * methods concat-fan across providers; single-row methods route to ONE
@@ -14728,12 +14814,15 @@ var NcOccupancyConditionSchema = object({
14728
14814
  * there is no second switch that can disagree with the first and every rule
14729
14815
  * authored before the decision migrates for free (`audioModeOf`):
14730
14816
  *
14731
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14732
- * classifier labels with one of them. No window, no percentage:
14733
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14734
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14735
- * the analyzer's (`classificationMinScore`, per device) a label only
14736
- * reaches this condition if the classifier was already confident enough.
14817
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14818
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14819
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
14820
+ * frames is the wrong question for a classifier that labels 1–3 frames
14821
+ * per episode. The count window is the brake that drops a single-frame
14822
+ * false positive; the rule's own `throttle` cooldown is the other. The
14823
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14824
+ * per device) — a label only reaches this condition if the classifier was
14825
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14737
14826
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14738
14827
  * the condition: at least `hitPercent`% of the samples over
14739
14828
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14760,14 +14849,22 @@ var NcOccupancyConditionSchema = object({
14760
14849
  * an operator who typed `dog` mean the same thing.
14761
14850
  */
14762
14851
  var NcAudioConditionSchema = object({
14763
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14852
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14764
14853
  labels: array(string().min(1)).min(1).optional(),
14765
14854
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14766
14855
  dbThreshold: number().min(-96).max(0).optional(),
14767
14856
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14768
14857
  hitPercent: number().int().min(1).max(100).default(60),
14769
14858
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14770
- samplingSeconds: number().int().min(1).max(300).default(10)
14859
+ samplingSeconds: number().int().min(1).max(300).default(10),
14860
+ /**
14861
+ * LABEL MODE: how many labelled frames must land inside
14862
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
14863
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
14864
+ */
14865
+ confirmHits: number().int().min(1).max(20).optional(),
14866
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
14867
+ confirmWindowSec: number().int().min(1).max(60).optional()
14771
14868
  });
14772
14869
  /**
14773
14870
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -16294,7 +16391,7 @@ var OauthIntegrationDescriptorSchema = object({
16294
16391
  */
16295
16392
  refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
16296
16393
  });
16297
- method(_void(), OauthIntegrationDescriptorSchema);
16394
+ method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
16298
16395
  /**
16299
16396
  * pipeline-analytics — device-scoped wrapper cap. Refines raw
16300
16397
  * per-frame detections emitted by the pipeline runner into tracked
@@ -17141,6 +17238,46 @@ var RecentTracksPageSchema = object({
17141
17238
  /** Cursor for the next page, or null when this page is the last. */
17142
17239
  nextCursor: string().nullable()
17143
17240
  });
17241
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
17242
+ var LIST_GROUPS_MAX_LIMIT = 100;
17243
+ var AnalyticsGroupRecordSchema = object({
17244
+ id: string(),
17245
+ deviceId: number().int(),
17246
+ openedAt: number().int(),
17247
+ closedAt: number().int(),
17248
+ timestamp: number().int(),
17249
+ memberCount: number().int(),
17250
+ memberTrackIds: array(string()).readonly(),
17251
+ className: string(),
17252
+ classes: array(string()).readonly(),
17253
+ /** Relative event-media path, or null when the group has no picture yet. */
17254
+ mediaUrl: string().nullable(),
17255
+ singleton: boolean()
17256
+ });
17257
+ var AnalyticsGroupMemberSchema = object({
17258
+ trackId: string(),
17259
+ deviceId: number().int(),
17260
+ className: string(),
17261
+ firstSeen: number().int(),
17262
+ lastSeen: number().int(),
17263
+ mediaUrl: string().nullable()
17264
+ });
17265
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
17266
+ var ListGroupsQueryInput = object({
17267
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
17268
+ deviceIds: array(number()),
17269
+ /** Window lower bound on `closedAt` (inclusive). */
17270
+ since: number().optional(),
17271
+ /** Window upper bound on `openedAt` (inclusive). */
17272
+ until: number().optional(),
17273
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
17274
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
17275
+ cursor: string().optional()
17276
+ });
17277
+ var ListGroupsPageSchema = object({
17278
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
17279
+ nextCursor: string().nullable()
17280
+ });
17144
17281
  var KeyEventQueryInput = object({
17145
17282
  deviceId: number(),
17146
17283
  /** Window lower bound (track firstSeen ≥ since). */
@@ -17216,7 +17353,9 @@ var TrackCascadeCountsSchema = object({
17216
17353
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
17217
17354
  plates: number().int(),
17218
17355
  /** Per-track CLIP search vectors removed (best-effort). */
17219
- embeddings: number().int()
17356
+ embeddings: number().int(),
17357
+ /** Group membership + group rows removed with their last member (best-effort). */
17358
+ groups: number().int()
17220
17359
  });
17221
17360
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
17222
17361
  var DiskReconcileCountsSchema = object({
@@ -17362,7 +17501,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17362
17501
  * stationary registry). Default false: the timeline lists passages,
17363
17502
  * not parking records (operator decision, 2026-08-15). */
17364
17503
  includeStationary: boolean().optional()
17365
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17504
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17505
+ deviceId: number(),
17506
+ groupId: string().min(1)
17507
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17366
17508
  kind: "mutation",
17367
17509
  auth: "admin"
17368
17510
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -17444,6 +17586,15 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17444
17586
  }), method(object({ jobId: string() }), RelocateJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
17445
17587
  kind: "mutation",
17446
17588
  auth: "admin"
17589
+ }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
17590
+ kind: "mutation",
17591
+ auth: "admin"
17592
+ }), method(object({}), array(RelocateJobSchema).readonly(), {
17593
+ kind: "query",
17594
+ auth: "admin"
17595
+ }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
17596
+ kind: "mutation",
17597
+ auth: "admin"
17447
17598
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
17448
17599
  kind: "query",
17449
17600
  auth: "admin"
@@ -17598,6 +17749,11 @@ object({
17598
17749
  "jpeg"
17599
17750
  ])
17600
17751
  });
17752
+ /**
17753
+ * Process-local frame identity. It is serializable so it can ride an in-process
17754
+ * capability call, but `registryId` deliberately prevents resolution in any
17755
+ * other process or execution group.
17756
+ */
17601
17757
  var FrameRefSchema = object({
17602
17758
  registryId: string().min(1),
17603
17759
  id: string().min(1),
@@ -17672,7 +17828,8 @@ var PipelineModelOptionSchema = object({
17672
17828
  sizeMB: number()
17673
17829
  })),
17674
17830
  group: ModelVariantGroupSchema.optional(),
17675
- legacy: boolean().optional()
17831
+ legacy: boolean().optional(),
17832
+ provider: ModelProviderIdSchema.optional()
17676
17833
  });
17677
17834
  var ConfigFieldBridge = custom();
17678
17835
  var PipelineAddonSchemaSchema = object({
@@ -19906,7 +20063,7 @@ method(object({
19906
20063
  * linking rather than produce an eternal token.
19907
20064
  */
19908
20065
  ttlSec: union([number().int().positive(), literal("never")]).optional()
19909
- }), object({ token: string() })), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable());
20066
+ }), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
19910
20067
  var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
19911
20068
  providerId: string().min(1),
19912
20069
  displayName: string().min(1),
@@ -20001,10 +20158,13 @@ var EvictResultSchema = object({
20001
20158
  /** True when the provider has nothing left it is willing to drop on this location. */
20002
20159
  exhausted: boolean()
20003
20160
  });
20004
- method(object({ locationId: string() }), EvictableUsageSchema), method(object({
20161
+ method(object({ locationId: string() }), EvictableUsageSchema, { auth: "admin" }), method(object({
20005
20162
  locationId: string(),
20006
20163
  targetBytes: number().int().positive()
20007
- }), EvictResultSchema, { kind: "mutation" });
20164
+ }), EvictResultSchema, {
20165
+ kind: "mutation",
20166
+ auth: "admin"
20167
+ });
20008
20168
  method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin" }), method(StorageMigrationInputSchema, object({ jobId: string() }), {
20009
20169
  kind: "mutation",
20010
20170
  auth: "admin"
@@ -20064,26 +20224,50 @@ var ReadChunkInputSchema = object({
20064
20224
  length: number()
20065
20225
  });
20066
20226
  var EndDownloadInputSchema = object({ downloadId: string() });
20067
- method(_void(), ProviderInfoSchema), method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }), method(object({
20227
+ method(_void(), ProviderInfoSchema, { auth: "admin" }), method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }), method(object({
20068
20228
  location: StorageLocationSchema,
20069
20229
  relativePath: string()
20070
- }), string()), method(object({
20230
+ }), string(), { auth: "admin" }), method(object({
20071
20231
  location: StorageLocationSchema,
20072
20232
  relativePath: string(),
20073
20233
  data: _instanceof(Uint8Array)
20074
- }), _void(), { kind: "mutation" }), method(object({
20234
+ }), _void(), {
20235
+ kind: "mutation",
20236
+ auth: "admin"
20237
+ }), method(object({
20075
20238
  location: StorageLocationSchema,
20076
20239
  relativePath: string()
20077
- }), _instanceof(Uint8Array)), method(object({
20240
+ }), _instanceof(Uint8Array), { auth: "admin" }), method(object({
20078
20241
  location: StorageLocationSchema,
20079
20242
  relativePath: string()
20080
- }), boolean()), method(object({
20243
+ }), boolean(), { auth: "admin" }), method(object({
20081
20244
  location: StorageLocationSchema,
20082
20245
  prefix: string().optional()
20083
- }), array(string()).readonly()), method(object({
20246
+ }), array(string()).readonly(), { auth: "admin" }), method(object({
20084
20247
  location: StorageLocationSchema,
20085
20248
  relativePath: string()
20086
- }), _void(), { kind: "mutation" }), method(object({ location: StorageLocationSchema }), number().nullable()), method(BeginUploadInputSchema, BeginUploadResultSchema, { kind: "mutation" }), method(WriteChunkInputSchema, _void(), { kind: "mutation" }), method(FinalizeUploadInputSchema, _void(), { kind: "mutation" }), method(AbortUploadInputSchema, _void(), { kind: "mutation" }), method(BeginDownloadInputSchema, BeginDownloadResultSchema, { kind: "mutation" }), method(ReadChunkInputSchema, _instanceof(Uint8Array)), method(EndDownloadInputSchema, _void(), { kind: "mutation" });
20249
+ }), _void(), {
20250
+ kind: "mutation",
20251
+ auth: "admin"
20252
+ }), method(object({ location: StorageLocationSchema }), number().nullable(), { auth: "admin" }), method(BeginUploadInputSchema, BeginUploadResultSchema, {
20253
+ kind: "mutation",
20254
+ auth: "admin"
20255
+ }), method(WriteChunkInputSchema, _void(), {
20256
+ kind: "mutation",
20257
+ auth: "admin"
20258
+ }), method(FinalizeUploadInputSchema, _void(), {
20259
+ kind: "mutation",
20260
+ auth: "admin"
20261
+ }), method(AbortUploadInputSchema, _void(), {
20262
+ kind: "mutation",
20263
+ auth: "admin"
20264
+ }), method(BeginDownloadInputSchema, BeginDownloadResultSchema, {
20265
+ kind: "mutation",
20266
+ auth: "admin"
20267
+ }), method(ReadChunkInputSchema, _instanceof(Uint8Array), { auth: "admin" }), method(EndDownloadInputSchema, _void(), {
20268
+ kind: "mutation",
20269
+ auth: "admin"
20270
+ });
20087
20271
  /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
20088
20272
  var ProfileSettingsSchemaBridge = unknown().nullable();
20089
20273
  var ProfileSettingsBagSchema = record(string(), unknown());
@@ -20385,7 +20569,8 @@ method(object({
20385
20569
  access: "create"
20386
20570
  }), method(object({ userId: string().optional() }), object({ optionsJSON: record(string(), unknown()) }), {
20387
20571
  kind: "mutation",
20388
- access: "view"
20572
+ access: "view",
20573
+ auth: "admin"
20389
20574
  }), method(object({
20390
20575
  /** Required — the user the assertion belongs to (verified). */
20391
20576
  userId: string(),
@@ -20393,10 +20578,12 @@ method(object({
20393
20578
  response: record(string(), unknown())
20394
20579
  }), object({ verified: boolean() }), {
20395
20580
  kind: "mutation",
20396
- access: "view"
20581
+ access: "view",
20582
+ auth: "admin"
20397
20583
  }), method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
20398
20584
  kind: "mutation",
20399
- access: "view"
20585
+ access: "view",
20586
+ auth: "admin"
20400
20587
  }), method(object({
20401
20588
  /** AuthenticationResponseJSON from the browser. */
20402
20589
  response: record(string(), unknown()) }), object({
@@ -20404,7 +20591,8 @@ response: record(string(), unknown()) }), object({
20404
20591
  userId: string().nullable()
20405
20592
  }), {
20406
20593
  kind: "mutation",
20407
- access: "view"
20594
+ access: "view",
20595
+ auth: "admin"
20408
20596
  }), method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }), method(object({
20409
20597
  userId: string(),
20410
20598
  credentialId: string()
@@ -20576,7 +20764,19 @@ var VectorStatsResultSchema = object({
20576
20764
  /** False when the backend ranks approximately. */
20577
20765
  exact: boolean()
20578
20766
  });
20579
- method(VectorDeclareIndexInputSchema, _void(), { kind: "mutation" }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, { kind: "mutation" }), method(VectorQueryInputSchema, VectorQueryResultSchema), method(VectorGetInputSchema, VectorGetResultSchema), method(VectorDeleteInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorStatsInputSchema, VectorStatsResultSchema);
20767
+ method(VectorDeclareIndexInputSchema, _void(), {
20768
+ kind: "mutation",
20769
+ auth: "admin"
20770
+ }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
20771
+ kind: "mutation",
20772
+ auth: "admin"
20773
+ }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
20774
+ kind: "mutation",
20775
+ auth: "admin"
20776
+ }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
20777
+ kind: "mutation",
20778
+ auth: "admin"
20779
+ }), method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" });
20580
20780
  var ClipSchema = object({
20581
20781
  /** Opaque, provider-namespaced id. The default provider encodes the time
20582
20782
  * window so `getClipPlayback` is self-contained (no event re-query). */
@@ -23029,7 +23229,27 @@ var MediaFileLiteSchema$1 = object({
23029
23229
  sizeBytes: number(),
23030
23230
  timestamp: number()
23031
23231
  });
23032
- method(_void(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
23232
+ method(object({
23233
+ /**
23234
+ * Inline {@link IdentitySchema.coverBase64} on every row.
23235
+ *
23236
+ * Default `false`, the same inversion `listRecentFaces` and
23237
+ * `listPlates` took on 2026-08-25 (see `include-crops-default.ts` for
23238
+ * why the burden belongs on the caller that WANTS the bytes). Measured
23239
+ * on the live hub the same day: four identities cost 40,979 B with the
23240
+ * covers inline, ~10 KB of base64 per row, on a query this UI mounts
23241
+ * four times and the viewer holds at `staleTime: 30_000`.
23242
+ *
23243
+ * Nothing loses its avatar: `coverMediaKey` is already on every row and
23244
+ * the `event-media` plane serves that key `immutable` with an ETag.
23245
+ *
23246
+ * **This is an INPUT field, so it does not reach the addon until the
23247
+ * next train** — the hub router validates cap inputs against its own
23248
+ * compiled Zod and strips a key it does not know. Until then the
23249
+ * provider sees `undefined`, which resolves to `false`: the cheap shape
23250
+ * is what ships, and the opt-in becomes reachable when the train lands.
23251
+ */
23252
+ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
23033
23253
  kind: "mutation",
23034
23254
  auth: "admin"
23035
23255
  }), method(object({
@@ -25917,7 +26137,14 @@ var PlateInfoSchema = object({
25917
26137
  plateBbox: BoundingBoxSchema.optional(),
25918
26138
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
25919
26139
  keyFrameMediaKey: string().optional(),
25920
- base64: string().optional()
26140
+ base64: string().optional(),
26141
+ /**
26142
+ * Same crop as a data-plane URL, always present when the plate has a stored
26143
+ * crop. `getPlateByTrack` returns this and never inlines JPEG; `listPlates`
26144
+ * and `searchPlates` inline the crop only when their `includeCrops` input is
26145
+ * left at its `true` default.
26146
+ */
26147
+ cropUrl: string().optional()
25921
26148
  });
25922
26149
  var MediaFileLiteSchema = object({
25923
26150
  key: string(),
@@ -25935,14 +26162,34 @@ var PlateClusterSchema = object({
25935
26162
  });
25936
26163
  method(object({
25937
26164
  deviceId: number().int().optional(),
25938
- limit: number().int().positive().optional()
26165
+ limit: number().int().positive().optional(),
26166
+ /**
26167
+ * Inline the base64 crop on every row. Default `true` — the existing
26168
+ * behaviour, kept so no caller breaks.
26169
+ *
26170
+ * Set `false` once the caller renders {@link PlateInfo.cropUrl}.
26171
+ * Measured on the live hub at the 500 rows the Plates view asks for:
26172
+ * 879,403 B and 27.7 s with the crops inline, against a few KiB of
26173
+ * metadata without them — and the browser then caches the images.
26174
+ *
26175
+ * This is the plate twin of `faceGallery.listRecentFaces`'s option;
26176
+ * plates were the one gallery list left without it.
26177
+ *
26178
+ * **This is an INPUT field, so it does not reach the addon until the
26179
+ * next train.** The hub router validates cap inputs against its own
26180
+ * compiled Zod and strips a key it does not know. Until the train
26181
+ * ships, sending `false` is harmless and keeps the crops inline.
26182
+ */
26183
+ includeCrops: boolean().optional()
25939
26184
  }).optional(), array(PlateInfoSchema).readonly()), method(object({
25940
26185
  deviceId: number().int(),
25941
26186
  trackId: string()
25942
26187
  }), PlateInfoSchema.nullable()), method(object({ plateId: string() }), array(MediaFileLiteSchema).readonly()), method(object({
25943
26188
  text: string().min(1),
25944
26189
  maxDistance: number().int().min(0).optional(),
25945
- limit: number().int().positive().optional()
26190
+ limit: number().int().positive().optional(),
26191
+ /** See `listPlates.includeCrops`. Default `true`. */
26192
+ includeCrops: boolean().optional()
25946
26193
  }), array(PlateInfoSchema).readonly()), method(object({
25947
26194
  maxDistance: number().int().min(0).optional(),
25948
26195
  minClusterSize: number().int().min(2).optional(),
@@ -25956,7 +26203,13 @@ method(object({
25956
26203
  }), method(object({ plateId: string() }), _void(), {
25957
26204
  kind: "mutation",
25958
26205
  auth: "admin"
25959
- }), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
26206
+ }), method(object({
26207
+ /** Inline {@link VehicleSchema.coverBase64} on every row. Default
26208
+ * `false` — the vehicle twin of `faceGallery.listIdentities`'s
26209
+ * option; `coverMediaKey` + the `event-media` plane carry the picture.
26210
+ * INPUT field: stripped by the hub router until the train ships, which
26211
+ * resolves to `false` and is exactly the intended default. */
26212
+ includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
25960
26213
  kind: "mutation",
25961
26214
  auth: "admin"
25962
26215
  }), method(object({
@@ -31439,6 +31692,12 @@ Object.freeze({
31439
31692
  addonId: null,
31440
31693
  access: "view"
31441
31694
  },
31695
+ "deviceManager.getBindingsBatch": {
31696
+ capName: "device-manager",
31697
+ capScope: "system",
31698
+ addonId: null,
31699
+ access: "view"
31700
+ },
31442
31701
  "deviceManager.getChildren": {
31443
31702
  capName: "device-manager",
31444
31703
  capScope: "system",
@@ -31499,6 +31758,12 @@ Object.freeze({
31499
31758
  addonId: null,
31500
31759
  access: "view"
31501
31760
  },
31761
+ "deviceManager.getLinkedDevicesBatch": {
31762
+ capName: "device-manager",
31763
+ capScope: "system",
31764
+ addonId: null,
31765
+ access: "view"
31766
+ },
31502
31767
  "deviceManager.getRoleDisplayDefaults": {
31503
31768
  capName: "device-manager",
31504
31769
  capScope: "system",
@@ -33203,6 +33468,12 @@ Object.freeze({
33203
33468
  addonId: null,
33204
33469
  access: "create"
33205
33470
  },
33471
+ "pipelineAnalytics.cancelRelocateMedia": {
33472
+ capName: "pipeline-analytics",
33473
+ capScope: "device",
33474
+ addonId: null,
33475
+ access: "create"
33476
+ },
33206
33477
  "pipelineAnalytics.cancelStorageMigrationMove": {
33207
33478
  capName: "pipeline-analytics",
33208
33479
  capScope: "device",
@@ -33269,6 +33540,12 @@ Object.freeze({
33269
33540
  addonId: null,
33270
33541
  access: "view"
33271
33542
  },
33543
+ "pipelineAnalytics.getGroup": {
33544
+ capName: "pipeline-analytics",
33545
+ capScope: "device",
33546
+ addonId: null,
33547
+ access: "view"
33548
+ },
33272
33549
  "pipelineAnalytics.getKeyEvents": {
33273
33550
  capName: "pipeline-analytics",
33274
33551
  capScope: "device",
@@ -33353,6 +33630,12 @@ Object.freeze({
33353
33630
  addonId: null,
33354
33631
  access: "view"
33355
33632
  },
33633
+ "pipelineAnalytics.listGroups": {
33634
+ capName: "pipeline-analytics",
33635
+ capScope: "device",
33636
+ addonId: null,
33637
+ access: "view"
33638
+ },
33356
33639
  "pipelineAnalytics.listOpsLog": {
33357
33640
  capName: "pipeline-analytics",
33358
33641
  capScope: "device",
@@ -33365,6 +33648,12 @@ Object.freeze({
33365
33648
  addonId: null,
33366
33649
  access: "view"
33367
33650
  },
33651
+ "pipelineAnalytics.listRelocateMediaJobs": {
33652
+ capName: "pipeline-analytics",
33653
+ capScope: "device",
33654
+ addonId: null,
33655
+ access: "view"
33656
+ },
33368
33657
  "pipelineAnalytics.listRetrainAnnotations": {
33369
33658
  capName: "pipeline-analytics",
33370
33659
  capScope: "device",
@@ -33443,6 +33732,12 @@ Object.freeze({
33443
33732
  addonId: null,
33444
33733
  access: "create"
33445
33734
  },
33735
+ "pipelineAnalytics.relocateMedia": {
33736
+ capName: "pipeline-analytics",
33737
+ capScope: "device",
33738
+ addonId: null,
33739
+ access: "create"
33740
+ },
33446
33741
  "pipelineAnalytics.restageRetrainTrack": {
33447
33742
  capName: "pipeline-analytics",
33448
33743
  capScope: "device",
@@ -36134,6 +36429,11 @@ Object.freeze({
36134
36429
  form: "single",
36135
36430
  optional: false
36136
36431
  }],
36432
+ "deviceManager.getBindingsBatch": [{
36433
+ name: "deviceIds",
36434
+ form: "array",
36435
+ optional: false
36436
+ }],
36137
36437
  "deviceManager.getChildren": [{
36138
36438
  name: "parentDeviceId",
36139
36439
  form: "single",
@@ -36179,6 +36479,11 @@ Object.freeze({
36179
36479
  form: "single",
36180
36480
  optional: false
36181
36481
  }],
36482
+ "deviceManager.getLinkedDevicesBatch": [{
36483
+ name: "deviceIds",
36484
+ form: "array",
36485
+ optional: false
36486
+ }],
36182
36487
  "deviceManager.getSettingsSchema": [{
36183
36488
  name: "deviceId",
36184
36489
  form: "single",
@@ -36199,6 +36504,11 @@ Object.freeze({
36199
36504
  form: "single",
36200
36505
  optional: false
36201
36506
  }],
36507
+ "deviceManager.listAll": [{
36508
+ name: "deviceIds",
36509
+ form: "array",
36510
+ optional: true
36511
+ }],
36202
36512
  "deviceManager.loadConfig": [{
36203
36513
  name: "deviceId",
36204
36514
  form: "single",
@@ -36772,6 +37082,11 @@ Object.freeze({
36772
37082
  form: "single",
36773
37083
  optional: false
36774
37084
  }],
37085
+ "pipelineAnalytics.getGroup": [{
37086
+ name: "deviceId",
37087
+ form: "single",
37088
+ optional: false
37089
+ }],
36775
37090
  "pipelineAnalytics.getKeyEvents": [{
36776
37091
  name: "deviceId",
36777
37092
  form: "single",
@@ -36827,6 +37142,11 @@ Object.freeze({
36827
37142
  form: "array",
36828
37143
  optional: false
36829
37144
  }],
37145
+ "pipelineAnalytics.listGroups": [{
37146
+ name: "deviceIds",
37147
+ form: "array",
37148
+ optional: false
37149
+ }],
36830
37150
  "pipelineAnalytics.listOpsLog": [{
36831
37151
  name: "deviceId",
36832
37152
  form: "single",
@@ -44281,7 +44601,10 @@ function sanitizeProfileSettings(profileId, raw) {
44281
44601
  showContainers: true,
44282
44602
  showSensors: true
44283
44603
  };
44284
- for (const plugin of GLANCES_PLUGINS) if (typeof bag[plugin.key] === "boolean") out[plugin.key] = bag[plugin.key];
44604
+ for (const plugin of GLANCES_PLUGINS) {
44605
+ const value = bag[plugin.key];
44606
+ if (typeof value === "boolean") out[plugin.key] = value;
44607
+ }
44285
44608
  return out;
44286
44609
  }
44287
44610
  function profileSettingsToArgs(profileId, settings) {