@camstack/addon-agent-ui 1.2.28 → 1.2.30

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 (2) hide show
  1. package/dist/addon.js +442 -113
  2. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -5802,6 +5802,13 @@ var BaseAddon = class {
5802
5802
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5803
5803
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5804
5804
  _registeredCapNames = [];
5805
+ /**
5806
+ * True only after `readAddonStore` actually answered. Constructor
5807
+ * defaults look like stored config when the store is down — a forked
5808
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5809
+ * mode, 2026-08-25) is not "the operator chose this".
5810
+ */
5811
+ settingsStoreReady = false;
5805
5812
  /** Default config values. Provided via constructor. */
5806
5813
  defaults;
5807
5814
  constructor(defaults) {
@@ -6202,7 +6209,9 @@ var BaseAddon = class {
6202
6209
  ];
6203
6210
  let lastErr;
6204
6211
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6205
- return await settings.readAddonStore() ?? {};
6212
+ const stored = await settings.readAddonStore() ?? {};
6213
+ this.settingsStoreReady = true;
6214
+ return stored;
6206
6215
  } catch (err) {
6207
6216
  lastErr = err;
6208
6217
  const msg = err instanceof Error ? err.message : String(err);
@@ -6210,6 +6219,7 @@ var BaseAddon = class {
6210
6219
  if (attempt === delaysMs.length) break;
6211
6220
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6212
6221
  }
6222
+ this.settingsStoreReady = false;
6213
6223
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6214
6224
  return {};
6215
6225
  }
@@ -6621,7 +6631,7 @@ function method(input, output, options) {
6621
6631
  input,
6622
6632
  output,
6623
6633
  kind: options?.kind ?? "query",
6624
- auth: options?.auth ?? "protected",
6634
+ ...options?.auth !== void 0 ? { auth: options.auth } : {},
6625
6635
  ...options?.access !== void 0 ? { access: options.access } : {},
6626
6636
  ...options?.caller !== void 0 ? { caller: options.caller } : {},
6627
6637
  timeoutMs: options?.timeoutMs
@@ -6647,8 +6657,17 @@ var adminUiCapability = {
6647
6657
  mode: "singleton",
6648
6658
  internal: true,
6649
6659
  methods: {
6650
- getStaticDir: method(_void(), StaticDirOutputSchema$1),
6651
- getVersion: method(_void(), VersionOutputSchema$1)
6660
+ /**
6661
+ * `internal: true` did not gate the mount (see the 2026-08-26 note on
6662
+ * `data-store-provider`) — both methods were reachable on the AppRouter
6663
+ * by ANY authenticated session at the default `auth: 'protected'`, and
6664
+ * `getStaticDir` leaks a hub filesystem path. The only caller is
6665
+ * `main.ts`'s static-file bootstrap, via `capRegistry.getSingletonForNode`
6666
+ * — never tRPC — so `auth: 'admin'` costs it nothing. The actual admin-ui
6667
+ * SPA assets are served as plain static files, unaffected by this gate.
6668
+ */
6669
+ getStaticDir: method(_void(), StaticDirOutputSchema$1, { auth: "admin" }),
6670
+ getVersion: method(_void(), VersionOutputSchema$1, { auth: "admin" })
6652
6671
  }
6653
6672
  };
6654
6673
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
@@ -6971,7 +6990,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
6971
6990
  }({});
6972
6991
  var StaticDirOutputSchema = object({ staticDir: string() });
6973
6992
  var VersionOutputSchema = object({ version: string() });
6974
- method(_void(), StaticDirOutputSchema), method(_void(), VersionOutputSchema);
6993
+ method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
6975
6994
  /**
6976
6995
  * device-ops — device-scoped cap that unifies the per-IDevice operations
6977
6996
  * previously routed through the `.device-ops` Moleculer bridge service.
@@ -7585,24 +7604,6 @@ var RecordingRetentionSchema = object({
7585
7604
  maxSizeGb: number().min(0).optional()
7586
7605
  });
7587
7606
  /**
7588
- * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7589
- * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7590
- * previews at. Five graduated steps; absent on a config = `standard` (the
7591
- * shipped default, matching `sheet-geometry`/`sheet-composer`).
7592
- *
7593
- * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7594
- * Each window's index sidecar carries its own tile dims, so a camera whose
7595
- * preset changed over time renders every historical window at the dims it was
7596
- * written with.
7597
- */
7598
- var ScrubThumbnailPresetSchema = _enum([
7599
- "minimal",
7600
- "low",
7601
- "standard",
7602
- "high",
7603
- "max"
7604
- ]);
7605
- /**
7606
7607
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7607
7608
  *
7608
7609
  * `bands` is the ONLY authored recording intent: what to record, when, and on
@@ -7610,7 +7611,11 @@ var ScrubThumbnailPresetSchema = _enum([
7610
7611
  * other field is a storage knob (profiles, segment length, retention, scrub).
7611
7612
  *
7612
7613
  * STRICT on purpose: the legacy authoring surface (`schedule`/`schedules`/
7613
- * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30.
7614
+ * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30,
7615
+ * and `scrubThumbnails` — a five-step fidelity knob for a sprite tier that was
7616
+ * deleted on 2026-07-24 and had ZERO consumers in the recorder — on 2026-08-25
7617
+ * (D62: a switch that writes a store nobody reads is worse than no switch).
7618
+ * Stored rows keep loading: the READ schema is `.strip()` (config-store.ts).
7614
7619
  * A stale caller must fail loudly — silently stripping its legacy intent would
7615
7620
  * persist a band-less config, i.e. silently stop recording the camera.
7616
7621
  */
@@ -7633,14 +7638,7 @@ var RecordingConfigSchema = object({
7633
7638
  * "off" is the absence of a covering band, never a band value.
7634
7639
  */
7635
7640
  bands: array(RecordingBandSchema).default([]),
7636
- retention: RecordingRetentionSchema.optional(),
7637
- /**
7638
- * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7639
- * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7640
- * windows only — existing sheets are immutable, and each window's index
7641
- * carries its own tile dims so mixed-preset history renders correctly.
7642
- */
7643
- scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7641
+ retention: RecordingRetentionSchema.optional()
7644
7642
  }).strict();
7645
7643
  /**
7646
7644
  * Entity-relocation job state (storage entity-routing spec, Phase 4).
@@ -7716,10 +7714,11 @@ var RelocateFootageInputSchema = object({
7716
7714
  * `RecordingConfig.enabled` or camera wrapper bindings. */
7717
7715
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
7718
7716
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
7719
- var StorageMigrationMediaMoveInputSchema = object({
7717
+ var RelocateMediaInputSchema = object({
7720
7718
  toLocationId: string(),
7721
7719
  throttleMbps: number().min(1).max(1e3).optional()
7722
- }).extend({ leaseId: string().min(1) });
7720
+ });
7721
+ var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
7723
7722
  /** The independently selectable logical storage classes. `recordings`
7724
7723
  * encompasses the high and mid segment profiles; `recordingsLow` is low
7725
7724
  * segments; `eventMedia` is post-analysis blobs. */
@@ -8014,7 +8013,26 @@ var LabelDefinitionSchema = object({
8014
8013
  description: string().optional(),
8015
8014
  icon: string().optional()
8016
8015
  });
8017
- var ClassMapDefinitionSchema = object({
8016
+ /**
8017
+ * Wire schema for a per-model CATALOG classMap override
8018
+ * (`ModelCatalogEntry.classMap` / `ModelConvertMetadata.classMap`) —
8019
+ * restricted to {@link CLASS_MAP_MACRO_TARGETS}, the only macros the
8020
+ * detection pipeline executor actually routes.
8021
+ *
8022
+ * This is deliberately a DIFFERENT, narrower shape than the general-purpose
8023
+ * `ClassMapDefinition` interface above (e.g. `IDetectionAddon.getClassMap()`
8024
+ * and the audio `YAMNET_TO_MACRO` catalog both use macro targets outside this
8025
+ * enum) — the two used to share the name `ClassMapDefinition`/
8026
+ * `ClassMapDefinitionSchema`, which made the schema-type-twin guard
8027
+ * (`scripts/check-schema-type-twins.ts`) flag them as a duplicated shape. They
8028
+ * are not: it is two different concepts colliding on a name. Keep this type
8029
+ * under its own name rather than reusing `ClassMapDefinition` — reusing it
8030
+ * would either narrow every `ClassMapDefinition` consumer to the four
8031
+ * detection macros (breaking `YAMNET_TO_MACRO`) or drop the validation this
8032
+ * schema exists for (see the "rejects a classMap whose target is not a
8033
+ * detection macro" test in `model-catalog-schema.test.ts`).
8034
+ */
8035
+ var DetectionCatalogClassMapSchema = object({
8018
8036
  mapping: record(string(), _enum([
8019
8037
  "person",
8020
8038
  "vehicle",
@@ -8106,6 +8124,12 @@ var ModelVariantGroupSchema = object({
8106
8124
  */
8107
8125
  resolution: number().int().positive().optional()
8108
8126
  });
8127
+ var ModelProviderIdSchema = _enum([
8128
+ "camstack",
8129
+ "frigate",
8130
+ "scrypted",
8131
+ "custom"
8132
+ ]);
8109
8133
  var ModelCatalogEntrySchema = object({
8110
8134
  id: string(),
8111
8135
  name: string(),
@@ -8203,11 +8227,17 @@ var ModelCatalogEntrySchema = object({
8203
8227
  */
8204
8228
  group: ModelVariantGroupSchema.optional(),
8205
8229
  /**
8230
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8231
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8232
+ * persisted before this field existed (`inferModelProvider` fills those).
8233
+ */
8234
+ provider: ModelProviderIdSchema.optional(),
8235
+ /**
8206
8236
  * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8207
8237
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8208
8238
  * labels already ARE the CamStack macros (Scrypted identity map).
8209
8239
  */
8210
- classMap: ClassMapDefinitionSchema.optional()
8240
+ classMap: DetectionCatalogClassMapSchema.optional()
8211
8241
  });
8212
8242
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8213
8243
  format: literal("openvino"),
@@ -8237,7 +8267,7 @@ var ModelConvertMetadataSchema = object({
8237
8267
  "segmentation"
8238
8268
  ]),
8239
8269
  faceAlignment: boolean().optional(),
8240
- classMap: ClassMapDefinitionSchema.optional()
8270
+ classMap: DetectionCatalogClassMapSchema.optional()
8241
8271
  });
8242
8272
  var ConvertResultSchema = object({
8243
8273
  entry: ModelCatalogEntrySchema,
@@ -9100,7 +9130,7 @@ var AddonPageDeclarationSchema = object({
9100
9130
  /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
9101
9131
  sectionLabel: string().optional()
9102
9132
  });
9103
- method(_void(), array(AddonPageDeclarationSchema).readonly());
9133
+ method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
9104
9134
  var AddonHttpRouteSchema = object({
9105
9135
  method: _enum([
9106
9136
  "GET",
@@ -9335,7 +9365,7 @@ var WidgetMetadataSchema = object({
9335
9365
  defaultColumns: number().int().min(1).max(12).default(6),
9336
9366
  defaultRows: number().int().min(1).max(12).default(1)
9337
9367
  });
9338
- method(_void(), array(WidgetMetadataSchema).readonly());
9368
+ method(_void(), array(WidgetMetadataSchema).readonly(), { auth: "admin" });
9339
9369
  /**
9340
9370
  * `addon-widgets` — system-scoped singleton aggregator cap. Public-facing
9341
9371
  * surface that admin-ui consumes through `useAddonWidgetsListWidgets()`.
@@ -10857,7 +10887,7 @@ var CustomModelDescriptorSchema = object({
10857
10887
  stepId: string(),
10858
10888
  entry: ModelCatalogEntrySchema
10859
10889
  });
10860
- method(_void(), array(CustomModelDescriptorSchema).readonly());
10890
+ method(_void(), array(CustomModelDescriptorSchema).readonly(), { auth: "admin" });
10861
10891
  /**
10862
10892
  * Query filter for settings-store collections.
10863
10893
  */
@@ -10944,7 +10974,8 @@ method(object({
10944
10974
  }), _void(), { kind: "mutation" }), method(object({
10945
10975
  namespace: string().optional(),
10946
10976
  collection: string(),
10947
- filter: QueryFilterSchema.optional()
10977
+ filter: QueryFilterSchema.optional(),
10978
+ columns: array(string()).readonly().optional()
10948
10979
  }), array(SettingsRecordSchema).readonly()), method(object({
10949
10980
  namespace: string().optional(),
10950
10981
  collection: string(),
@@ -11007,46 +11038,87 @@ var EngineInfoSchema = object({
11007
11038
  kind: _enum(["relational", "vector"]),
11008
11039
  displayName: string()
11009
11040
  });
11010
- method(_void(), EngineInfoSchema), method(object({
11041
+ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
11011
11042
  namespace: string().optional(),
11012
11043
  collection: string(),
11013
11044
  key: string()
11014
- }), unknown()), method(object({
11045
+ }), unknown(), { auth: "admin" }), method(object({
11015
11046
  namespace: string().optional(),
11016
11047
  collection: string(),
11017
11048
  key: string(),
11018
11049
  value: unknown()
11019
- }), _void(), { kind: "mutation" }), method(object({
11050
+ }), _void(), {
11051
+ kind: "mutation",
11052
+ auth: "admin"
11053
+ }), method(object({
11020
11054
  namespace: string().optional(),
11021
11055
  collection: string(),
11022
- filter: QueryFilterSchema.optional()
11023
- }), array(SettingsRecordSchema).readonly()), method(object({
11056
+ filter: QueryFilterSchema.optional(),
11057
+ /**
11058
+ * SQL-level column projection — MUST mirror `settings-store.query`.
11059
+ *
11060
+ * ⚠ An earlier version of this comment blamed Zod stripping, and that
11061
+ * was wrong — corrected 2026-08-26 after the hop map
11062
+ * (`docs/design/2026-08-26-mappa-hop-argomenti.md`) traced the path.
11063
+ * There is **no Zod parse at all** between the door and the engine: the
11064
+ * dispatcher forwards the payload verbatim and UDS carries it whole. A
11065
+ * field declared here reaches `SqliteSettingsBackend` either way.
11066
+ *
11067
+ * What actually lost `columns` was the THIRD declaration of this shape:
11068
+ * `SettingsQueryInput` in `interfaces/storage.ts`, a hand-written TS
11069
+ * interface the engine destructures from. The field existed on both
11070
+ * schemas and the engine still never read it, because nothing checks a
11071
+ * registered provider against `InferProvider<cap>` —
11072
+ * `ProviderRegistration.provider` is typed `object`.
11073
+ *
11074
+ * It is declared here anyway, and must stay in step with
11075
+ * `settings-store.query`: a caller reading only the cap definitions has
11076
+ * to be able to see that this call carries a projection.
11077
+ * `data-door-schema-parity.spec.ts` keeps the two aligned.
11078
+ */
11079
+ columns: array(string()).readonly().optional()
11080
+ }), array(SettingsRecordSchema).readonly(), { auth: "admin" }), method(object({
11024
11081
  namespace: string().optional(),
11025
11082
  collection: string(),
11026
11083
  record: SettingsRecordSchema
11027
- }), _void(), { kind: "mutation" }), method(object({
11084
+ }), _void(), {
11085
+ kind: "mutation",
11086
+ auth: "admin"
11087
+ }), method(object({
11028
11088
  namespace: string().optional(),
11029
11089
  collection: string(),
11030
11090
  id: string(),
11031
11091
  data: record(string(), unknown())
11032
- }), _void(), { kind: "mutation" }), method(object({
11092
+ }), _void(), {
11093
+ kind: "mutation",
11094
+ auth: "admin"
11095
+ }), method(object({
11033
11096
  namespace: string().optional(),
11034
11097
  collection: string(),
11035
11098
  key: string()
11036
- }), _void(), { kind: "mutation" }), method(object({
11099
+ }), _void(), {
11100
+ kind: "mutation",
11101
+ auth: "admin"
11102
+ }), method(object({
11037
11103
  namespace: string().optional(),
11038
11104
  collection: string(),
11039
11105
  filter: MutationFilterSchema
11040
- }), object({ deleted: number().int() }), { kind: "mutation" }), method(object({
11106
+ }), object({ deleted: number().int() }), {
11107
+ kind: "mutation",
11108
+ auth: "admin"
11109
+ }), method(object({
11041
11110
  namespace: string().optional(),
11042
11111
  collection: string(),
11043
11112
  filter: MutationFilterSchema,
11044
11113
  data: record(string(), unknown())
11045
- }), object({ updated: number().int() }), { kind: "mutation" }), method(object({
11114
+ }), object({ updated: number().int() }), {
11115
+ kind: "mutation",
11116
+ auth: "admin"
11117
+ }), method(object({
11046
11118
  namespace: string().optional(),
11047
11119
  collection: string(),
11048
11120
  filter: QueryFilterSchema.optional()
11049
- }), number()), method(object({
11121
+ }), number(), { auth: "admin" }), method(object({
11050
11122
  namespace: string().optional(),
11051
11123
  collection: string(),
11052
11124
  field: string(),
@@ -11056,15 +11128,18 @@ method(_void(), EngineInfoSchema), method(object({
11056
11128
  }), array(object({
11057
11129
  bucket: number().int(),
11058
11130
  count: number().int()
11059
- })).readonly()), method(object({
11131
+ })).readonly(), { auth: "admin" }), method(object({
11060
11132
  namespace: string().optional(),
11061
11133
  collection: string()
11062
- }), boolean()), method(object({
11134
+ }), boolean(), { auth: "admin" }), method(object({
11063
11135
  namespace: string().optional(),
11064
11136
  collection: string(),
11065
11137
  columns: array(CollectionColumnSchema).readonly(),
11066
11138
  indexes: array(CollectionIndexSchema).readonly().optional()
11067
- }), _void(), { kind: "mutation" });
11139
+ }), _void(), {
11140
+ kind: "mutation",
11141
+ auth: "admin"
11142
+ });
11068
11143
  /**
11069
11144
  * shm ring usage stats for a `frameSink: 'shm'` decoder session —
11070
11145
  * exposed via `decoder.getShmStats` so downstream consumers can
@@ -11730,6 +11805,27 @@ var LinkedDeviceSchema = object({
11730
11805
  features: array(string()),
11731
11806
  producesTrackedEvents: boolean().optional()
11732
11807
  });
11808
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
11809
+ * The batch answer needs the tag; the single-device answer already has it
11810
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
11811
+ var LinkedDevicesForDeviceSchema = object({
11812
+ deviceId: number(),
11813
+ mode: LinkedDevicesModeSchema,
11814
+ devices: array(LinkedDeviceSchema)
11815
+ });
11816
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
11817
+ * `getAllBindings` all answer in. Declared once: three copies of the same
11818
+ * object literal is exactly how the three drift apart. */
11819
+ var DeviceBindingsForDeviceSchema = object({
11820
+ deviceId: number(),
11821
+ entries: array(object({
11822
+ capName: string(),
11823
+ kind: _enum(["native", "wrapped"]),
11824
+ providerAddonId: string(),
11825
+ providerNodeId: string(),
11826
+ nativeAddonId: string()
11827
+ }))
11828
+ });
11733
11829
  var SavedDeviceRowSchema = object({
11734
11830
  /** Numeric id reserved at allocateDeviceId time. */
11735
11831
  id: number(),
@@ -11955,11 +12051,25 @@ method(object({
11955
12051
  projection: _enum(["full", "slim"]).optional(),
11956
12052
  /** Return only camera devices. Filtering server-side instead of
11957
12053
  * shipping 293 rows to find 12. */
11958
- isCamera: boolean().optional()
12054
+ isCamera: boolean().optional(),
12055
+ /**
12056
+ * Return only these device ids. For the caller that already KNOWS the
12057
+ * handful it wants and needs a field the id-bearing answer does not
12058
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12059
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12060
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12061
+ * refetches on the reconcile interval, on a phone.
12062
+ *
12063
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12064
+ * keys rather than rejecting them (verified against the live hub
12065
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12066
+ * it answers today and the caller filters as it already does.
12067
+ */
12068
+ deviceIds: array(number()).optional()
11959
12069
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
11960
12070
  mode: LinkedDevicesModeSchema,
11961
12071
  devices: array(LinkedDeviceSchema)
11962
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12072
+ })), 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({
11963
12073
  deviceId: number(),
11964
12074
  values: record(string(), unknown())
11965
12075
  }), object({ success: literal(true) }), {
@@ -11986,25 +12096,7 @@ method(object({
11986
12096
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
11987
12097
  kind: "mutation",
11988
12098
  auth: "admin"
11989
- }), method(object({ deviceId: number() }), object({
11990
- deviceId: number(),
11991
- entries: array(object({
11992
- capName: string(),
11993
- kind: _enum(["native", "wrapped"]),
11994
- providerAddonId: string(),
11995
- providerNodeId: string(),
11996
- nativeAddonId: string()
11997
- }))
11998
- })), method(object({}), array(object({
11999
- deviceId: number(),
12000
- entries: array(object({
12001
- capName: string(),
12002
- kind: _enum(["native", "wrapped"]),
12003
- providerAddonId: string(),
12004
- providerNodeId: string(),
12005
- nativeAddonId: string()
12006
- }))
12007
- }))), method(object({
12099
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12008
12100
  deviceId: number(),
12009
12101
  capName: string(),
12010
12102
  wrapperAddonId: string(),
@@ -12175,7 +12267,7 @@ method(object({
12175
12267
  crop: _instanceof(Uint8Array),
12176
12268
  width: number(),
12177
12269
  height: number()
12178
- }), EmbeddingResultSchema), method(object({ text: string() }), EmbeddingResultSchema), method(_void(), EmbeddingInfoSchema);
12270
+ }), EmbeddingResultSchema, { auth: "admin" }), method(object({ text: string() }), EmbeddingResultSchema, { auth: "admin" }), method(_void(), EmbeddingInfoSchema, { auth: "admin" });
12179
12271
  /**
12180
12272
  * filesystem-browse — per-node capability for browsing the node's local
12181
12273
  * filesystem. Reads are unconfined (whole filesystem, from `/` down); WRITES
@@ -12468,19 +12560,22 @@ method(LlmGenerateBaseInputSchema.extend({
12468
12560
  runtime: ManagedRuntimeConfigSchema,
12469
12561
  /** The managed profile's timeout, threaded by the hub provider. */
12470
12562
  timeoutMs: number().int().positive().optional()
12471
- }), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
12563
+ }), LlmGenerateResultSchema, {
12564
+ kind: "mutation",
12565
+ auth: "admin"
12566
+ }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
12472
12567
  kind: "mutation",
12473
12568
  auth: "admin"
12474
12569
  }), method(object({}), _void(), {
12475
12570
  kind: "mutation",
12476
12571
  auth: "admin"
12477
- }), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
12572
+ }), method(object({}), LlmRuntimeStatusSchema, { auth: "admin" }), method(object({ model: ManagedModelRefSchema }), _void(), {
12478
12573
  kind: "mutation",
12479
12574
  auth: "admin"
12480
12575
  }), method(object({ file: string() }), _void(), {
12481
12576
  kind: "mutation",
12482
12577
  auth: "admin"
12483
- }), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
12578
+ }), method(object({}), array(LlmNodeModelSchema), { auth: "admin" }), method(object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" });
12484
12579
  /**
12485
12580
  * `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
12486
12581
  * methods concat-fan across providers; single-row methods route to ONE
@@ -14376,12 +14471,15 @@ var NcOccupancyConditionSchema = object({
14376
14471
  * there is no second switch that can disagree with the first and every rule
14377
14472
  * authored before the decision migrates for free (`audioModeOf`):
14378
14473
  *
14379
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14380
- * classifier labels with one of them. No window, no percentage:
14381
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14382
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14383
- * the analyzer's (`classificationMinScore`, per device) a label only
14384
- * reaches this condition if the classifier was already confident enough.
14474
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14475
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14476
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
14477
+ * frames is the wrong question for a classifier that labels 1–3 frames
14478
+ * per episode. The count window is the brake that drops a single-frame
14479
+ * false positive; the rule's own `throttle` cooldown is the other. The
14480
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14481
+ * per device) — a label only reaches this condition if the classifier was
14482
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14385
14483
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14386
14484
  * the condition: at least `hitPercent`% of the samples over
14387
14485
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14408,14 +14506,22 @@ var NcOccupancyConditionSchema = object({
14408
14506
  * an operator who typed `dog` mean the same thing.
14409
14507
  */
14410
14508
  var NcAudioConditionSchema = object({
14411
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14509
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14412
14510
  labels: array(string().min(1)).min(1).optional(),
14413
14511
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14414
14512
  dbThreshold: number().min(-96).max(0).optional(),
14415
14513
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14416
14514
  hitPercent: number().int().min(1).max(100).default(60),
14417
14515
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14418
- samplingSeconds: number().int().min(1).max(300).default(10)
14516
+ samplingSeconds: number().int().min(1).max(300).default(10),
14517
+ /**
14518
+ * LABEL MODE: how many labelled frames must land inside
14519
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
14520
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
14521
+ */
14522
+ confirmHits: number().int().min(1).max(20).optional(),
14523
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
14524
+ confirmWindowSec: number().int().min(1).max(60).optional()
14419
14525
  });
14420
14526
  /**
14421
14527
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -15942,7 +16048,7 @@ var OauthIntegrationDescriptorSchema = object({
15942
16048
  */
15943
16049
  refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
15944
16050
  });
15945
- method(_void(), OauthIntegrationDescriptorSchema);
16051
+ method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
15946
16052
  /**
15947
16053
  * pipeline-analytics — device-scoped wrapper cap. Refines raw
15948
16054
  * per-frame detections emitted by the pipeline runner into tracked
@@ -16789,6 +16895,46 @@ var RecentTracksPageSchema = object({
16789
16895
  /** Cursor for the next page, or null when this page is the last. */
16790
16896
  nextCursor: string().nullable()
16791
16897
  });
16898
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
16899
+ var LIST_GROUPS_MAX_LIMIT = 100;
16900
+ var AnalyticsGroupRecordSchema = object({
16901
+ id: string(),
16902
+ deviceId: number().int(),
16903
+ openedAt: number().int(),
16904
+ closedAt: number().int(),
16905
+ timestamp: number().int(),
16906
+ memberCount: number().int(),
16907
+ memberTrackIds: array(string()).readonly(),
16908
+ className: string(),
16909
+ classes: array(string()).readonly(),
16910
+ /** Relative event-media path, or null when the group has no picture yet. */
16911
+ mediaUrl: string().nullable(),
16912
+ singleton: boolean()
16913
+ });
16914
+ var AnalyticsGroupMemberSchema = object({
16915
+ trackId: string(),
16916
+ deviceId: number().int(),
16917
+ className: string(),
16918
+ firstSeen: number().int(),
16919
+ lastSeen: number().int(),
16920
+ mediaUrl: string().nullable()
16921
+ });
16922
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
16923
+ var ListGroupsQueryInput = object({
16924
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
16925
+ deviceIds: array(number()),
16926
+ /** Window lower bound on `closedAt` (inclusive). */
16927
+ since: number().optional(),
16928
+ /** Window upper bound on `openedAt` (inclusive). */
16929
+ until: number().optional(),
16930
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
16931
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
16932
+ cursor: string().optional()
16933
+ });
16934
+ var ListGroupsPageSchema = object({
16935
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
16936
+ nextCursor: string().nullable()
16937
+ });
16792
16938
  var KeyEventQueryInput = object({
16793
16939
  deviceId: number(),
16794
16940
  /** Window lower bound (track firstSeen ≥ since). */
@@ -16864,7 +17010,9 @@ var TrackCascadeCountsSchema = object({
16864
17010
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
16865
17011
  plates: number().int(),
16866
17012
  /** Per-track CLIP search vectors removed (best-effort). */
16867
- embeddings: number().int()
17013
+ embeddings: number().int(),
17014
+ /** Group membership + group rows removed with their last member (best-effort). */
17015
+ groups: number().int()
16868
17016
  });
16869
17017
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
16870
17018
  var DiskReconcileCountsSchema = object({
@@ -17010,7 +17158,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17010
17158
  * stationary registry). Default false: the timeline lists passages,
17011
17159
  * not parking records (operator decision, 2026-08-15). */
17012
17160
  includeStationary: boolean().optional()
17013
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17161
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17162
+ deviceId: number(),
17163
+ groupId: string().min(1)
17164
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17014
17165
  kind: "mutation",
17015
17166
  auth: "admin"
17016
17167
  }), 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({
@@ -17092,6 +17243,15 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17092
17243
  }), method(object({ jobId: string() }), RelocateJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
17093
17244
  kind: "mutation",
17094
17245
  auth: "admin"
17246
+ }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
17247
+ kind: "mutation",
17248
+ auth: "admin"
17249
+ }), method(object({}), array(RelocateJobSchema).readonly(), {
17250
+ kind: "query",
17251
+ auth: "admin"
17252
+ }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
17253
+ kind: "mutation",
17254
+ auth: "admin"
17095
17255
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
17096
17256
  kind: "query",
17097
17257
  auth: "admin"
@@ -17246,6 +17406,11 @@ object({
17246
17406
  "jpeg"
17247
17407
  ])
17248
17408
  });
17409
+ /**
17410
+ * Process-local frame identity. It is serializable so it can ride an in-process
17411
+ * capability call, but `registryId` deliberately prevents resolution in any
17412
+ * other process or execution group.
17413
+ */
17249
17414
  var FrameRefSchema = object({
17250
17415
  registryId: string().min(1),
17251
17416
  id: string().min(1),
@@ -17320,7 +17485,8 @@ var PipelineModelOptionSchema = object({
17320
17485
  sizeMB: number()
17321
17486
  })),
17322
17487
  group: ModelVariantGroupSchema.optional(),
17323
- legacy: boolean().optional()
17488
+ legacy: boolean().optional(),
17489
+ provider: ModelProviderIdSchema.optional()
17324
17490
  });
17325
17491
  var ConfigFieldBridge = custom();
17326
17492
  var PipelineAddonSchemaSchema = object({
@@ -19410,7 +19576,7 @@ method(object({
19410
19576
  * linking rather than produce an eternal token.
19411
19577
  */
19412
19578
  ttlSec: union([number().int().positive(), literal("never")]).optional()
19413
- }), object({ token: string() })), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable());
19579
+ }), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
19414
19580
  var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
19415
19581
  providerId: string().min(1),
19416
19582
  displayName: string().min(1),
@@ -19505,10 +19671,13 @@ var EvictResultSchema = object({
19505
19671
  /** True when the provider has nothing left it is willing to drop on this location. */
19506
19672
  exhausted: boolean()
19507
19673
  });
19508
- method(object({ locationId: string() }), EvictableUsageSchema), method(object({
19674
+ method(object({ locationId: string() }), EvictableUsageSchema, { auth: "admin" }), method(object({
19509
19675
  locationId: string(),
19510
19676
  targetBytes: number().int().positive()
19511
- }), EvictResultSchema, { kind: "mutation" });
19677
+ }), EvictResultSchema, {
19678
+ kind: "mutation",
19679
+ auth: "admin"
19680
+ });
19512
19681
  method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin" }), method(StorageMigrationInputSchema, object({ jobId: string() }), {
19513
19682
  kind: "mutation",
19514
19683
  auth: "admin"
@@ -19568,26 +19737,50 @@ var ReadChunkInputSchema = object({
19568
19737
  length: number()
19569
19738
  });
19570
19739
  var EndDownloadInputSchema = object({ downloadId: string() });
19571
- method(_void(), ProviderInfoSchema), method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }), method(object({
19740
+ method(_void(), ProviderInfoSchema, { auth: "admin" }), method(object({ config: record(string(), unknown()) }), TestLocationResultSchema, { auth: "admin" }), method(object({
19572
19741
  location: StorageLocationSchema,
19573
19742
  relativePath: string()
19574
- }), string()), method(object({
19743
+ }), string(), { auth: "admin" }), method(object({
19575
19744
  location: StorageLocationSchema,
19576
19745
  relativePath: string(),
19577
19746
  data: _instanceof(Uint8Array)
19578
- }), _void(), { kind: "mutation" }), method(object({
19747
+ }), _void(), {
19748
+ kind: "mutation",
19749
+ auth: "admin"
19750
+ }), method(object({
19579
19751
  location: StorageLocationSchema,
19580
19752
  relativePath: string()
19581
- }), _instanceof(Uint8Array)), method(object({
19753
+ }), _instanceof(Uint8Array), { auth: "admin" }), method(object({
19582
19754
  location: StorageLocationSchema,
19583
19755
  relativePath: string()
19584
- }), boolean()), method(object({
19756
+ }), boolean(), { auth: "admin" }), method(object({
19585
19757
  location: StorageLocationSchema,
19586
19758
  prefix: string().optional()
19587
- }), array(string()).readonly()), method(object({
19759
+ }), array(string()).readonly(), { auth: "admin" }), method(object({
19588
19760
  location: StorageLocationSchema,
19589
19761
  relativePath: string()
19590
- }), _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" });
19762
+ }), _void(), {
19763
+ kind: "mutation",
19764
+ auth: "admin"
19765
+ }), method(object({ location: StorageLocationSchema }), number().nullable(), { auth: "admin" }), method(BeginUploadInputSchema, BeginUploadResultSchema, {
19766
+ kind: "mutation",
19767
+ auth: "admin"
19768
+ }), method(WriteChunkInputSchema, _void(), {
19769
+ kind: "mutation",
19770
+ auth: "admin"
19771
+ }), method(FinalizeUploadInputSchema, _void(), {
19772
+ kind: "mutation",
19773
+ auth: "admin"
19774
+ }), method(AbortUploadInputSchema, _void(), {
19775
+ kind: "mutation",
19776
+ auth: "admin"
19777
+ }), method(BeginDownloadInputSchema, BeginDownloadResultSchema, {
19778
+ kind: "mutation",
19779
+ auth: "admin"
19780
+ }), method(ReadChunkInputSchema, _instanceof(Uint8Array), { auth: "admin" }), method(EndDownloadInputSchema, _void(), {
19781
+ kind: "mutation",
19782
+ auth: "admin"
19783
+ });
19591
19784
  /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
19592
19785
  var ProfileSettingsSchemaBridge = unknown().nullable();
19593
19786
  var ProfileSettingsBagSchema = record(string(), unknown());
@@ -19845,7 +20038,8 @@ method(object({
19845
20038
  access: "create"
19846
20039
  }), method(object({ userId: string().optional() }), object({ optionsJSON: record(string(), unknown()) }), {
19847
20040
  kind: "mutation",
19848
- access: "view"
20041
+ access: "view",
20042
+ auth: "admin"
19849
20043
  }), method(object({
19850
20044
  /** Required — the user the assertion belongs to (verified). */
19851
20045
  userId: string(),
@@ -19853,10 +20047,12 @@ method(object({
19853
20047
  response: record(string(), unknown())
19854
20048
  }), object({ verified: boolean() }), {
19855
20049
  kind: "mutation",
19856
- access: "view"
20050
+ access: "view",
20051
+ auth: "admin"
19857
20052
  }), method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
19858
20053
  kind: "mutation",
19859
- access: "view"
20054
+ access: "view",
20055
+ auth: "admin"
19860
20056
  }), method(object({
19861
20057
  /** AuthenticationResponseJSON from the browser. */
19862
20058
  response: record(string(), unknown()) }), object({
@@ -19864,7 +20060,8 @@ response: record(string(), unknown()) }), object({
19864
20060
  userId: string().nullable()
19865
20061
  }), {
19866
20062
  kind: "mutation",
19867
- access: "view"
20063
+ access: "view",
20064
+ auth: "admin"
19868
20065
  }), method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }), method(object({
19869
20066
  userId: string(),
19870
20067
  credentialId: string()
@@ -20036,7 +20233,19 @@ var VectorStatsResultSchema = object({
20036
20233
  /** False when the backend ranks approximately. */
20037
20234
  exact: boolean()
20038
20235
  });
20039
- 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);
20236
+ method(VectorDeclareIndexInputSchema, _void(), {
20237
+ kind: "mutation",
20238
+ auth: "admin"
20239
+ }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
20240
+ kind: "mutation",
20241
+ auth: "admin"
20242
+ }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
20243
+ kind: "mutation",
20244
+ auth: "admin"
20245
+ }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
20246
+ kind: "mutation",
20247
+ auth: "admin"
20248
+ }), method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" });
20040
20249
  var ClipSchema = object({
20041
20250
  /** Opaque, provider-namespaced id. The default provider encodes the time
20042
20251
  * window so `getClipPlayback` is self-contained (no event re-query). */
@@ -21759,7 +21968,27 @@ var MediaFileLiteSchema$1 = object({
21759
21968
  sizeBytes: number(),
21760
21969
  timestamp: number()
21761
21970
  });
21762
- method(_void(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
21971
+ method(object({
21972
+ /**
21973
+ * Inline {@link IdentitySchema.coverBase64} on every row.
21974
+ *
21975
+ * Default `false`, the same inversion `listRecentFaces` and
21976
+ * `listPlates` took on 2026-08-25 (see `include-crops-default.ts` for
21977
+ * why the burden belongs on the caller that WANTS the bytes). Measured
21978
+ * on the live hub the same day: four identities cost 40,979 B with the
21979
+ * covers inline, ~10 KB of base64 per row, on a query this UI mounts
21980
+ * four times and the viewer holds at `staleTime: 30_000`.
21981
+ *
21982
+ * Nothing loses its avatar: `coverMediaKey` is already on every row and
21983
+ * the `event-media` plane serves that key `immutable` with an ETag.
21984
+ *
21985
+ * **This is an INPUT field, so it does not reach the addon until the
21986
+ * next train** — the hub router validates cap inputs against its own
21987
+ * compiled Zod and strips a key it does not know. Until then the
21988
+ * provider sees `undefined`, which resolves to `false`: the cheap shape
21989
+ * is what ships, and the opt-in becomes reachable when the train lands.
21990
+ */
21991
+ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly()), method(object({ name: string().min(1) }), IdentitySchema, {
21763
21992
  kind: "mutation",
21764
21993
  auth: "admin"
21765
21994
  }), method(object({
@@ -23904,7 +24133,14 @@ var PlateInfoSchema = object({
23904
24133
  plateBbox: BoundingBoxSchema.optional(),
23905
24134
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
23906
24135
  keyFrameMediaKey: string().optional(),
23907
- base64: string().optional()
24136
+ base64: string().optional(),
24137
+ /**
24138
+ * Same crop as a data-plane URL, always present when the plate has a stored
24139
+ * crop. `getPlateByTrack` returns this and never inlines JPEG; `listPlates`
24140
+ * and `searchPlates` inline the crop only when their `includeCrops` input is
24141
+ * left at its `true` default.
24142
+ */
24143
+ cropUrl: string().optional()
23908
24144
  });
23909
24145
  var MediaFileLiteSchema = object({
23910
24146
  key: string(),
@@ -23922,14 +24158,34 @@ var PlateClusterSchema = object({
23922
24158
  });
23923
24159
  method(object({
23924
24160
  deviceId: number().int().optional(),
23925
- limit: number().int().positive().optional()
24161
+ limit: number().int().positive().optional(),
24162
+ /**
24163
+ * Inline the base64 crop on every row. Default `true` — the existing
24164
+ * behaviour, kept so no caller breaks.
24165
+ *
24166
+ * Set `false` once the caller renders {@link PlateInfo.cropUrl}.
24167
+ * Measured on the live hub at the 500 rows the Plates view asks for:
24168
+ * 879,403 B and 27.7 s with the crops inline, against a few KiB of
24169
+ * metadata without them — and the browser then caches the images.
24170
+ *
24171
+ * This is the plate twin of `faceGallery.listRecentFaces`'s option;
24172
+ * plates were the one gallery list left without it.
24173
+ *
24174
+ * **This is an INPUT field, so it does not reach the addon until the
24175
+ * next train.** The hub router validates cap inputs against its own
24176
+ * compiled Zod and strips a key it does not know. Until the train
24177
+ * ships, sending `false` is harmless and keeps the crops inline.
24178
+ */
24179
+ includeCrops: boolean().optional()
23926
24180
  }).optional(), array(PlateInfoSchema).readonly()), method(object({
23927
24181
  deviceId: number().int(),
23928
24182
  trackId: string()
23929
24183
  }), PlateInfoSchema.nullable()), method(object({ plateId: string() }), array(MediaFileLiteSchema).readonly()), method(object({
23930
24184
  text: string().min(1),
23931
24185
  maxDistance: number().int().min(0).optional(),
23932
- limit: number().int().positive().optional()
24186
+ limit: number().int().positive().optional(),
24187
+ /** See `listPlates.includeCrops`. Default `true`. */
24188
+ includeCrops: boolean().optional()
23933
24189
  }), array(PlateInfoSchema).readonly()), method(object({
23934
24190
  maxDistance: number().int().min(0).optional(),
23935
24191
  minClusterSize: number().int().min(2).optional(),
@@ -23943,7 +24199,13 @@ method(object({
23943
24199
  }), method(object({ plateId: string() }), _void(), {
23944
24200
  kind: "mutation",
23945
24201
  auth: "admin"
23946
- }), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
24202
+ }), method(object({
24203
+ /** Inline {@link VehicleSchema.coverBase64} on every row. Default
24204
+ * `false` — the vehicle twin of `faceGallery.listIdentities`'s
24205
+ * option; `coverMediaKey` + the `event-media` plane carry the picture.
24206
+ * INPUT field: stripped by the hub router until the train ships, which
24207
+ * resolves to `false` and is exactly the intended default. */
24208
+ includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
23947
24209
  kind: "mutation",
23948
24210
  auth: "admin"
23949
24211
  }), method(object({
@@ -27544,6 +27806,12 @@ Object.freeze({
27544
27806
  addonId: null,
27545
27807
  access: "view"
27546
27808
  },
27809
+ "deviceManager.getBindingsBatch": {
27810
+ capName: "device-manager",
27811
+ capScope: "system",
27812
+ addonId: null,
27813
+ access: "view"
27814
+ },
27547
27815
  "deviceManager.getChildren": {
27548
27816
  capName: "device-manager",
27549
27817
  capScope: "system",
@@ -27604,6 +27872,12 @@ Object.freeze({
27604
27872
  addonId: null,
27605
27873
  access: "view"
27606
27874
  },
27875
+ "deviceManager.getLinkedDevicesBatch": {
27876
+ capName: "device-manager",
27877
+ capScope: "system",
27878
+ addonId: null,
27879
+ access: "view"
27880
+ },
27607
27881
  "deviceManager.getRoleDisplayDefaults": {
27608
27882
  capName: "device-manager",
27609
27883
  capScope: "system",
@@ -29308,6 +29582,12 @@ Object.freeze({
29308
29582
  addonId: null,
29309
29583
  access: "create"
29310
29584
  },
29585
+ "pipelineAnalytics.cancelRelocateMedia": {
29586
+ capName: "pipeline-analytics",
29587
+ capScope: "device",
29588
+ addonId: null,
29589
+ access: "create"
29590
+ },
29311
29591
  "pipelineAnalytics.cancelStorageMigrationMove": {
29312
29592
  capName: "pipeline-analytics",
29313
29593
  capScope: "device",
@@ -29374,6 +29654,12 @@ Object.freeze({
29374
29654
  addonId: null,
29375
29655
  access: "view"
29376
29656
  },
29657
+ "pipelineAnalytics.getGroup": {
29658
+ capName: "pipeline-analytics",
29659
+ capScope: "device",
29660
+ addonId: null,
29661
+ access: "view"
29662
+ },
29377
29663
  "pipelineAnalytics.getKeyEvents": {
29378
29664
  capName: "pipeline-analytics",
29379
29665
  capScope: "device",
@@ -29458,6 +29744,12 @@ Object.freeze({
29458
29744
  addonId: null,
29459
29745
  access: "view"
29460
29746
  },
29747
+ "pipelineAnalytics.listGroups": {
29748
+ capName: "pipeline-analytics",
29749
+ capScope: "device",
29750
+ addonId: null,
29751
+ access: "view"
29752
+ },
29461
29753
  "pipelineAnalytics.listOpsLog": {
29462
29754
  capName: "pipeline-analytics",
29463
29755
  capScope: "device",
@@ -29470,6 +29762,12 @@ Object.freeze({
29470
29762
  addonId: null,
29471
29763
  access: "view"
29472
29764
  },
29765
+ "pipelineAnalytics.listRelocateMediaJobs": {
29766
+ capName: "pipeline-analytics",
29767
+ capScope: "device",
29768
+ addonId: null,
29769
+ access: "view"
29770
+ },
29473
29771
  "pipelineAnalytics.listRetrainAnnotations": {
29474
29772
  capName: "pipeline-analytics",
29475
29773
  capScope: "device",
@@ -29548,6 +29846,12 @@ Object.freeze({
29548
29846
  addonId: null,
29549
29847
  access: "create"
29550
29848
  },
29849
+ "pipelineAnalytics.relocateMedia": {
29850
+ capName: "pipeline-analytics",
29851
+ capScope: "device",
29852
+ addonId: null,
29853
+ access: "create"
29854
+ },
29551
29855
  "pipelineAnalytics.restageRetrainTrack": {
29552
29856
  capName: "pipeline-analytics",
29553
29857
  capScope: "device",
@@ -32239,6 +32543,11 @@ Object.freeze({
32239
32543
  form: "single",
32240
32544
  optional: false
32241
32545
  }],
32546
+ "deviceManager.getBindingsBatch": [{
32547
+ name: "deviceIds",
32548
+ form: "array",
32549
+ optional: false
32550
+ }],
32242
32551
  "deviceManager.getChildren": [{
32243
32552
  name: "parentDeviceId",
32244
32553
  form: "single",
@@ -32284,6 +32593,11 @@ Object.freeze({
32284
32593
  form: "single",
32285
32594
  optional: false
32286
32595
  }],
32596
+ "deviceManager.getLinkedDevicesBatch": [{
32597
+ name: "deviceIds",
32598
+ form: "array",
32599
+ optional: false
32600
+ }],
32287
32601
  "deviceManager.getSettingsSchema": [{
32288
32602
  name: "deviceId",
32289
32603
  form: "single",
@@ -32304,6 +32618,11 @@ Object.freeze({
32304
32618
  form: "single",
32305
32619
  optional: false
32306
32620
  }],
32621
+ "deviceManager.listAll": [{
32622
+ name: "deviceIds",
32623
+ form: "array",
32624
+ optional: true
32625
+ }],
32307
32626
  "deviceManager.loadConfig": [{
32308
32627
  name: "deviceId",
32309
32628
  form: "single",
@@ -32877,6 +33196,11 @@ Object.freeze({
32877
33196
  form: "single",
32878
33197
  optional: false
32879
33198
  }],
33199
+ "pipelineAnalytics.getGroup": [{
33200
+ name: "deviceId",
33201
+ form: "single",
33202
+ optional: false
33203
+ }],
32880
33204
  "pipelineAnalytics.getKeyEvents": [{
32881
33205
  name: "deviceId",
32882
33206
  form: "single",
@@ -32932,6 +33256,11 @@ Object.freeze({
32932
33256
  form: "array",
32933
33257
  optional: false
32934
33258
  }],
33259
+ "pipelineAnalytics.listGroups": [{
33260
+ name: "deviceIds",
33261
+ form: "array",
33262
+ optional: false
33263
+ }],
32935
33264
  "pipelineAnalytics.listOpsLog": [{
32936
33265
  name: "deviceId",
32937
33266
  form: "single",
@@ -34148,7 +34477,7 @@ var AgentUIAddon = class extends BaseAddon {
34148
34477
  capability: adminUiCapability,
34149
34478
  provider: {
34150
34479
  getStaticDir: async () => ({ staticDir: path.resolve(__dirname) }),
34151
- getVersion: async () => ({ version: "1.2.28" })
34480
+ getVersion: async () => ({ version: "1.2.30" })
34152
34481
  }
34153
34482
  }];
34154
34483
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-agent-ui",
3
- "version": "1.2.28",
3
+ "version": "1.2.30",
4
4
  "description": "Agent UI — lightweight status dashboard served by every agent node",
5
5
  "keywords": [
6
6
  "camstack",