@camstack/addon-provider-rademacher 0.1.6 → 0.1.7

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 +672 -287
  2. package/dist/addon.mjs +672 -287
  3. package/package.json +1 -1
package/dist/addon.mjs CHANGED
@@ -5626,7 +5626,7 @@ function preprocess(fn, schema) {
5626
5626
  });
5627
5627
  }
5628
5628
  //#endregion
5629
- //#region ../types/dist/sleep-CZDdRBua.mjs
5629
+ //#region ../types/dist/sleep-BC9Yqte7.mjs
5630
5630
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
5631
5631
  EventCategory["SystemBoot"] = "system.boot";
5632
5632
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -5812,6 +5812,18 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
5812
5812
  */
5813
5813
  EventCategory["PipelineCameraUpdated"] = "pipeline.camera-updated";
5814
5814
  /**
5815
+ * The cluster camera-source OWNER changed (`clusterRoles.ingestNode`).
5816
+ * Emitted by addon-pipeline-orchestrator whenever it (re)derives node
5817
+ * capabilities — at boot, on agent online/offline, and on an ingest-node
5818
+ * flip. Carries the resolved `ownerNodeId`. The stream-broker consumes it to
5819
+ * keep its ingest-owner-gate decision current WITHOUT a per-`ensureBroker`
5820
+ * cross-process `getIngestOwner` query (push the authority's decision instead
5821
+ * of polling it on the hot path). Idempotent state — re-emitted on every
5822
+ * topology change, so a dropped event self-heals on the next one (plus the
5823
+ * broker's long backstop reconcile query).
5824
+ */
5825
+ EventCategory["PipelineIngestOwnerChanged"] = "pipeline.ingest-owner-changed";
5826
+ /**
5815
5827
  * Periodic snapshot of per-node pipeline-runner load
5816
5828
  * (`RunnerLocalLoad`). Emitted ~1Hz by every runner so UI dashboards
5817
5829
  * subscribe instead of polling `pipelineRunner.getLocalLoad`.
@@ -6335,10 +6347,6 @@ function hydrateField(field, values) {
6335
6347
  };
6336
6348
  }
6337
6349
  const rawValue = storedValue !== void 0 ? storedValue : defaultValue !== void 0 ? defaultValue : null;
6338
- if (field.type === "password") return {
6339
- ...field,
6340
- value: ""
6341
- };
6342
6350
  const value = field.type === "textarea" && field.isJson && rawValue !== null && typeof rawValue === "object" ? JSON.stringify(rawValue, null, 2) : rawValue;
6343
6351
  return {
6344
6352
  ...field,
@@ -7722,10 +7730,25 @@ function method(input, output, options) {
7722
7730
  timeoutMs: options?.timeoutMs
7723
7731
  };
7724
7732
  }
7733
+ /**
7734
+ * A wrapper/system-only method: served exclusively by the cap's system-level
7735
+ * provider (`InferProvider`), and OPTIONAL on `InferNativeProvider` so per-device
7736
+ * driver natives don't stub out a wrapper concern (e.g. a cross-device cache
7737
+ * overview). The `systemOnly: true` literal is what `InferNativeProvider` keys on.
7738
+ */
7739
+ function systemMethod(input, output, options) {
7740
+ return {
7741
+ ...method(input, output, options),
7742
+ systemOnly: true
7743
+ };
7744
+ }
7725
7745
  /** Shorthand to define an event schema */
7726
7746
  function event(data) {
7727
7747
  return { data };
7728
7748
  }
7749
+ var StaticDirOutputSchema$1 = object({ staticDir: string() });
7750
+ var VersionOutputSchema$1 = object({ version: string() });
7751
+ method(_void(), StaticDirOutputSchema$1), method(_void(), VersionOutputSchema$1);
7729
7752
  var StaticDirOutputSchema = object({ staticDir: string() });
7730
7753
  var VersionOutputSchema = object({ version: string() });
7731
7754
  method(_void(), StaticDirOutputSchema), method(_void(), VersionOutputSchema);
@@ -7907,6 +7930,36 @@ var ModelFormatsSchema = object({
7907
7930
  tflite: ModelFormatEntrySchema.optional(),
7908
7931
  pt: ModelFormatEntrySchema.optional()
7909
7932
  });
7933
+ /**
7934
+ * Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
7935
+ * the reduced `PipelineModelOption` returned in `pipeline.getSchema()` so the
7936
+ * grouped Family→Tier→Variant picker renders identically in the config UI and
7937
+ * in the pipeline/device steppers. The flat `id` stays the source of truth for
7938
+ * resolution/download/persistence; this is a presentation overlay resolved back
7939
+ * to an `id`.
7940
+ */
7941
+ var ModelVariantGroupSchema = object({
7942
+ /** Top-level family, e.g. `yolo26` (later `d-fine`, `rf-detr`). */
7943
+ family: string(),
7944
+ /** Size within the family, e.g. `n` | `s` | `m` | `l`. */
7945
+ tier: string(),
7946
+ /** Quantization axis. Omit ⇒ the fp32 base build. */
7947
+ precision: _enum(["fp32", "int8"]).optional(),
7948
+ /**
7949
+ * Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
7950
+ * latency-optimized export (e.g. ReLU-activation variant) — the slot the
7951
+ * future performance variants plug into.
7952
+ */
7953
+ optimization: _enum(["standard", "fast"]).optional(),
7954
+ /**
7955
+ * Input-resolution axis (square input side, px). Omit ⇒ the family's native
7956
+ * resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
7957
+ * cheap latency lever — especially on Apple ANE and the Intel N100 — at a
7958
+ * small-object accuracy cost. Mirrors the model's `inputSize` but lifted onto
7959
+ * the group so the selector can offer it as a variant axis.
7960
+ */
7961
+ resolution: number().int().positive().optional()
7962
+ });
7910
7963
  var ModelCatalogEntrySchema = object({
7911
7964
  id: string(),
7912
7965
  name: string(),
@@ -7936,7 +7989,43 @@ var ModelCatalogEntrySchema = object({
7936
7989
  * Auxiliary files required at runtime (labels JSON, charset dict, etc.).
7937
7990
  * Downloaded into the same modelsDir alongside the model file.
7938
7991
  */
7939
- extraFiles: array(ModelExtraFileSchema).readonly().optional()
7992
+ extraFiles: array(ModelExtraFileSchema).readonly().optional(),
7993
+ /**
7994
+ * LEGACY entry — retained in the catalog so a persisted operator selection
7995
+ * still RESOLVES (and can be re-activated), but hidden from the selectable
7996
+ * model list and excluded from the auto format-default pick. Set on the
7997
+ * superseded / consolidated models (older lineages, redundant fp16 IRs) so
7998
+ * the active lineup stays the coherent curated ladder without deleting a
7999
+ * model anyone may still be pinned to. `resolveModelForFormat` keeps honoring
8000
+ * an explicit legacy id that has a build for the node's format.
8001
+ */
8002
+ legacy: boolean().optional(),
8003
+ /**
8004
+ * Measured quality/latency metadata — populated from the benchmark addon on
8005
+ * the real node classes. Absent = not yet measured (most entries today; the
8006
+ * catalog historically carried only `sizeMB`, a poor cross-architecture
8007
+ * speed proxy). `p95LatencyMs` is keyed by node class (e.g. `n100`, `mac`).
8008
+ */
8009
+ metrics: object({
8010
+ map50: number().optional(),
8011
+ p95LatencyMs: record(string(), number()).optional()
8012
+ }).optional(),
8013
+ /**
8014
+ * SPDX-ish license id of the model weights (e.g. `AGPL-3.0` for Ultralytics
8015
+ * YOLO26, `GPL-3.0` for YOLOv9, `Apache-2.0` for D-FINE/RF-DETR). Matters for
8016
+ * the retraining addon and any future commercial distribution.
8017
+ */
8018
+ license: string().optional(),
8019
+ /**
8020
+ * Variant-selector grouping. The UI groups models by `family` + `tier` and
8021
+ * offers `precision` / `optimization` as variant axes WITHIN a tier — so all
8022
+ * of a family's sizes and quantizations collapse into one grouped picker
8023
+ * instead of a flat list of `yolo26s`, `yolo26s-int8`, … Absent ⇒ ungrouped
8024
+ * (legacy / custom models) — never shown in the grouped selector. The flat
8025
+ * `id` stays the source of truth for resolution/download/persistence; grouping
8026
+ * is a presentation overlay resolved back to an `id`.
8027
+ */
8028
+ group: ModelVariantGroupSchema.optional()
7940
8029
  });
7941
8030
  var ConvertTargetSchema = discriminatedUnion("format", [object({
7942
8031
  format: literal("openvino"),
@@ -7997,8 +8086,8 @@ var RecordingModeSchema = _enum([
7997
8086
  "onAudioThreshold"
7998
8087
  ]);
7999
8088
  /**
8000
- * First-class, authoritative per-camera storage mode — the netta choice the UI
8001
- * reads directly (never inferred from `rules`):
8089
+ * First-class, authoritative per-camera storage mode — the explicit choice the
8090
+ * UI reads directly (never inferred from `rules`):
8002
8091
  * - `off` — not recording.
8003
8092
  * - `events` — record only around triggers (motion / audio threshold),
8004
8093
  * with pre/post-buffer.
@@ -10161,26 +10250,13 @@ onBrightnessChanged: { data: object({
10161
10250
  */
10162
10251
  runtimeState: BrightnessStatusSchema
10163
10252
  };
10253
+ /** Stream delivery format. (Relocated from the retired `streaming-engine` cap.) */
10164
10254
  var StreamFormatSchema = _enum([
10165
10255
  "webrtc",
10166
10256
  "hls",
10167
10257
  "mjpeg",
10168
10258
  "rtsp"
10169
10259
  ]);
10170
- var StreamInfoSchema = object({
10171
- streamId: string(),
10172
- format: StreamFormatSchema,
10173
- url: string().nullable(),
10174
- active: boolean()
10175
- });
10176
- method(object({
10177
- streamId: string(),
10178
- sourceUrl: string(),
10179
- codec: string().optional()
10180
- }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), _void(), { kind: "mutation" }), method(object({
10181
- streamId: string(),
10182
- format: StreamFormatSchema
10183
- }), string().nullable()), method(_void(), array(StreamInfoSchema));
10184
10260
  var RtspRestreamEntrySchema = object({
10185
10261
  brokerId: string(),
10186
10262
  url: string(),
@@ -11048,37 +11124,7 @@ var consumablesCapability = {
11048
11124
  scope: "device",
11049
11125
  deviceNative: true,
11050
11126
  mode: "singleton",
11051
- deviceTypes: [
11052
- DeviceType.Camera,
11053
- DeviceType.Hub,
11054
- DeviceType.Light,
11055
- DeviceType.Siren,
11056
- DeviceType.Switch,
11057
- DeviceType.Sensor,
11058
- DeviceType.Thermostat,
11059
- DeviceType.Button,
11060
- DeviceType.EventEmitter,
11061
- DeviceType.Update,
11062
- DeviceType.Generic,
11063
- DeviceType.Notifier,
11064
- DeviceType.Script,
11065
- DeviceType.Automation,
11066
- DeviceType.Lock,
11067
- DeviceType.Cover,
11068
- DeviceType.Valve,
11069
- DeviceType.Humidifier,
11070
- DeviceType.WaterHeater,
11071
- DeviceType.Fan,
11072
- DeviceType.MediaPlayer,
11073
- DeviceType.AlarmPanel,
11074
- DeviceType.Control,
11075
- DeviceType.Presence,
11076
- DeviceType.Weather,
11077
- DeviceType.Vacuum,
11078
- DeviceType.LawnMower,
11079
- DeviceType.Container,
11080
- DeviceType.Image
11081
- ],
11127
+ deviceTypes: Object.values(DeviceType),
11082
11128
  deviceConfig: { ui: {
11083
11129
  kind: "widget",
11084
11130
  widgetId: "host/consumables-panel",
@@ -12536,7 +12582,7 @@ var BoundingBoxSchema = object({
12536
12582
  w: number(),
12537
12583
  h: number()
12538
12584
  });
12539
- var SpatialDetectionSchema = object({
12585
+ object({
12540
12586
  class: string(),
12541
12587
  originalClass: string(),
12542
12588
  score: number(),
@@ -12671,7 +12717,6 @@ var PipelineDefaultStepSchema = lazy(() => object({
12671
12717
  enabled: boolean(),
12672
12718
  modelId: string(),
12673
12719
  children: array(PipelineDefaultStepSchema).readonly(),
12674
- engine: PipelineEngineChoiceSchema.optional(),
12675
12720
  group: string().optional(),
12676
12721
  settings: record(string(), unknown()).optional()
12677
12722
  }));
@@ -12696,7 +12741,9 @@ var PipelineModelOptionSchema = object({
12696
12741
  formats: record(string(), object({
12697
12742
  downloaded: boolean(),
12698
12743
  sizeMB: number()
12699
- }))
12744
+ })),
12745
+ group: ModelVariantGroupSchema.optional(),
12746
+ legacy: boolean().optional()
12700
12747
  });
12701
12748
  var ConfigFieldBridge = custom();
12702
12749
  var PipelineAddonSchemaSchema = object({
@@ -12710,6 +12757,7 @@ var PipelineAddonSchemaSchema = object({
12710
12757
  defaultModelId: string(),
12711
12758
  defaultModelIdByFormat: record(string(), string()).optional(),
12712
12759
  enabledByDefault: boolean().optional(),
12760
+ backfillIntoExistingOverrides: boolean().optional(),
12713
12761
  defaultConfidence: number(),
12714
12762
  group: string().optional(),
12715
12763
  configSchema: array(ConfigFieldBridge).readonly().optional()
@@ -12726,11 +12774,6 @@ var PipelineSchemaSchema = object({
12726
12774
  selectedEngine: PipelineEngineChoiceSchema,
12727
12775
  slots: array(PipelineSlotSchemaSchema).readonly()
12728
12776
  });
12729
- var DetectorOutputSchema = object({
12730
- detections: array(SpatialDetectionSchema).readonly(),
12731
- inferenceMs: number(),
12732
- modelId: string()
12733
- });
12734
12777
  var EngineProvisioningSchema = object({
12735
12778
  runtimeId: _enum([
12736
12779
  "onnx",
@@ -12747,15 +12790,42 @@ var EngineProvisioningSchema = object({
12747
12790
  ]),
12748
12791
  progress: number().optional(),
12749
12792
  error: string().optional(),
12750
- nextRetryAt: number().optional()
12793
+ nextRetryAt: number().optional(),
12794
+ /**
12795
+ * Gate A (config-correctness gate at engine change): human-readable
12796
+ * config issues surfaced EAGERLY when the node's engine changes — model
12797
+ * substitutions ("chose X, running Y") and zero-build steps ("no model
12798
+ * has a <format> build"). Additive/optional: informational only, never
12799
+ * enforced here — `assertEngineReady` (readiness) still gates inference.
12800
+ * Absent/empty when the node-default tree resolves cleanly.
12801
+ */
12802
+ configIssues: array(string()).optional()
12751
12803
  });
12752
12804
  var PipelineStepInputSchema = lazy(() => object({
12753
12805
  addonId: string(),
12754
- modelId: string(),
12806
+ modelId: string().optional(),
12755
12807
  enabled: boolean().default(true),
12756
12808
  children: array(PipelineStepInputSchema).optional(),
12757
12809
  settings: record(string(), unknown()).optional()
12758
12810
  }));
12811
+ var ModelSubstitutionSchema = object({
12812
+ addonId: string(),
12813
+ chosen: string(),
12814
+ running: string(),
12815
+ format: string()
12816
+ });
12817
+ var PipelineValidationIssueSchema = object({
12818
+ addonId: string(),
12819
+ kind: _enum(["unknown-addon", "no-format-build"]),
12820
+ detail: string()
12821
+ });
12822
+ var PipelineValidationResultSchema = object({
12823
+ ok: boolean(),
12824
+ issues: array(PipelineValidationIssueSchema).readonly(),
12825
+ substitutions: array(ModelSubstitutionSchema).readonly(),
12826
+ /** The node's `currentEngine.format` this validation ran against. */
12827
+ format: string()
12828
+ });
12759
12829
  var ReferenceImageEntrySchema = object({
12760
12830
  filename: string(),
12761
12831
  stepIds: array(string()).readonly().optional()
@@ -12826,7 +12896,13 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12826
12896
  })) }), object({ success: literal(true) }), {
12827
12897
  kind: "mutation",
12828
12898
  auth: "admin"
12829
- }), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
12899
+ }), method(object({ nodeId: string() }), object({
12900
+ success: literal(true),
12901
+ clearedDevices: number()
12902
+ }), {
12903
+ kind: "mutation",
12904
+ auth: "admin"
12905
+ }), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(object({ steps: array(PipelineStepInputSchema) }), PipelineValidationResultSchema), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
12830
12906
  name: string(),
12831
12907
  steps: array(PipelineTemplateStepSchema).readonly(),
12832
12908
  engine: PipelineEngineChoiceSchema
@@ -12843,10 +12919,6 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12843
12919
  modelId: string(),
12844
12920
  format: ModelFormatSchema$1
12845
12921
  }), object({ success: literal(true) }), { kind: "mutation" }), method(object({
12846
- addonId: string(),
12847
- frame: FrameInputSchema,
12848
- config: record(string(), unknown()).optional()
12849
- }), DetectorOutputSchema), method(object({
12850
12922
  engine: PipelineEngineChoiceSchema.optional(),
12851
12923
  steps: array(PipelineStepInputSchema).min(1),
12852
12924
  frame: FrameInputSchema.optional(),
@@ -13025,6 +13097,25 @@ var zonesCapability = {
13025
13097
  runtimeState: object({ zones: array(ZoneSchema).readonly() })
13026
13098
  };
13027
13099
  /**
13100
+ * A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
13101
+ * decode worker resolves it against the RETAINED native frame's real pixel dims,
13102
+ * so the caller supplies only the detection-res bbox divided by the detection
13103
+ * dims — no native resolution to plumb.
13104
+ */
13105
+ var NativeCropBboxSchema = object({
13106
+ x: number(),
13107
+ y: number(),
13108
+ w: number(),
13109
+ h: number()
13110
+ });
13111
+ /** Result of a best-effort native-resolution crop (`getNativeCrop`). */
13112
+ var NativeCropResultSchema = object({
13113
+ /** Packed rgb (24-bit) pixels of the crop. */
13114
+ bytes: _instanceof(Uint8Array),
13115
+ width: number().int().positive(),
13116
+ height: number().int().positive()
13117
+ });
13118
+ /**
13028
13119
  * Per-camera tunable ranges + defaults. Single source of truth used
13029
13120
  * by both the Zod data schema (validation + default fallback) and
13030
13121
  * the device settings UI (slider min/max/step). Touch one place and
@@ -13119,6 +13210,13 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
13119
13210
  kind: literal("remote-restream"),
13120
13211
  /** The camera's source-owner node (slice 1: always the hub). */
13121
13212
  ownerNodeId: string(),
13213
+ /**
13214
+ * The owner's LAN-reachable host, resolved by the orchestrator from the
13215
+ * per-node `reachableHost` override (Cluster UI). When present the runner
13216
+ * dials THIS host for the owner's restream, in preference to the
13217
+ * `CAMSTACK_HUB_URL`-derived default. Absent → auto-detect fallback.
13218
+ */
13219
+ ownerReachableHost: string().optional(),
13122
13220
  /** Operator override for the owner host the runner dials. */
13123
13221
  hubHostnameOverride: string().optional()
13124
13222
  })]).describe("Per-camera frame-source mode for the runner (P2c)");
@@ -13127,13 +13225,11 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
13127
13225
  * specific runner instance via `attachCamera`. Carries everything the
13128
13226
  * runner needs to subscribe to the local broker and execute inference.
13129
13227
  *
13130
- * Stateless-pipeline model: the full pipeline content (`engine`, `steps`,
13131
- * optional `audio`) travels with the attach payload. The runner keeps it
13132
- * in RAM for the lifetime of the attach — on rebalance, edit, or
13133
- * restart the orchestrator re-sends the latest snapshot.
13134
- *
13135
- * `engine`/`steps`/`audio` are optional during the additive migration
13136
- * window; once orchestrator + UI are migrated they become required.
13228
+ * Stateless-pipeline model: the pipeline content (`steps`, optional
13229
+ * `audio`) travels with the attach payload. The runner keeps it in RAM
13230
+ * for the lifetime of the attach — on rebalance, edit, or restart the
13231
+ * orchestrator re-sends the latest snapshot. Engine is NOT carried: it is
13232
+ * node-local, resolved by the executing runner at dispatch time.
13137
13233
  */
13138
13234
  var RunnerCameraConfigSchema = object({
13139
13235
  deviceId: number(),
@@ -13184,14 +13280,11 @@ var RunnerCameraConfigSchema = object({
13184
13280
  */
13185
13281
  motionSources: MotionSourcesSchema.default(["analyzer"]),
13186
13282
  pipelineEnabled: boolean().default(true),
13187
- /** Engine choice for video steps (runtime+backend+format). */
13188
- engine: PipelineEngineChoiceSchema.optional(),
13189
13283
  /** Ordered tree of video steps. Absent → runner skips video detection. */
13190
13284
  steps: array(PipelineStepInputSchema).readonly().optional(),
13191
13285
  /** Audio classification branch. `enabled:false` disables, null skips. */
13192
13286
  audio: object({
13193
- engine: PipelineEngineChoiceSchema,
13194
- modelId: string(),
13287
+ modelId: string().optional(),
13195
13288
  enabled: boolean()
13196
13289
  }).nullable().optional(),
13197
13290
  /**
@@ -13278,7 +13371,11 @@ var RunnerLocalMetricsSchema = object({
13278
13371
  avgInferenceTimeMs: number(),
13279
13372
  queueDepth: number()
13280
13373
  });
13281
- method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number()).readonly());
13374
+ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number()).readonly()), method(object({
13375
+ handle: FrameHandleSchema,
13376
+ bbox: NativeCropBboxSchema,
13377
+ maxWidth: number().int().positive().optional()
13378
+ }), NativeCropResultSchema.nullable());
13282
13379
  /**
13283
13380
  * Hardware / firmware motion sensor cap — binary detected state plus
13284
13381
  * a timestamp of the last observation. Distinct from
@@ -16209,7 +16306,9 @@ var AddonPageDeclarationSchema$1 = object({
16209
16306
  icon: string(),
16210
16307
  path: string(),
16211
16308
  remoteName: string(),
16212
- bundle: string()
16309
+ bundle: string(),
16310
+ section: string().optional(),
16311
+ sectionLabel: string().optional()
16213
16312
  });
16214
16313
  var AddonPageInfoSchema = object({
16215
16314
  addonId: string(),
@@ -16249,7 +16348,18 @@ var AddonPageDeclarationSchema = object({
16249
16348
  * the static-file route can compute an mtime-based cache-buster URL
16250
16349
  * without a separate filesystem stat.
16251
16350
  */
16252
- bundle: string()
16351
+ bundle: string(),
16352
+ /**
16353
+ * Sidebar section this page docks into. Well-known ids: `'detection'`,
16354
+ * `'cluster'`, `'administration'` — the page renders inside that group.
16355
+ * Any OTHER string creates (or joins) a custom section rendered after
16356
+ * the built-in groups; its label comes from `sectionLabel` (first
16357
+ * declaration wins), falling back to the id. Absent → the legacy
16358
+ * "Addon Pages" group.
16359
+ */
16360
+ section: string().optional(),
16361
+ /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
16362
+ sectionLabel: string().optional()
16253
16363
  });
16254
16364
  method(_void(), array(AddonPageDeclarationSchema).readonly());
16255
16365
  var AddonHttpRouteSchema = object({
@@ -16465,6 +16575,17 @@ var WidgetMetadataSchema = object({
16465
16575
  deviceContext: boolean().default(false),
16466
16576
  integrationContext: boolean().default(false)
16467
16577
  }),
16578
+ /**
16579
+ * Loadable BEFORE authentication. The normal widget registry listing
16580
+ * (`addon-widgets.listWidgets`) is auth-gated, so a pre-auth surface
16581
+ * (the login page) cannot discover a widget through it. A widget that
16582
+ * declares `preAuth: true` marks itself as safe to mount on a pre-auth
16583
+ * screen — it is surfaced through the PUBLIC `auth.listLoginMethods`
16584
+ * login-method contribution channel (see `login-method.cap.ts`) rather
16585
+ * than the authenticated registry, and its bundle is served by the
16586
+ * public `/api/addon-widgets/:addonId/*` static route. Defaults false.
16587
+ */
16588
+ preAuth: boolean().optional().default(false),
16468
16589
  /** Dashboard placement HINTS (operator can override per instance). */
16469
16590
  defaultSize: WidgetSizeEnum.default("md"),
16470
16591
  allowedSizes: array(WidgetSizeEnum).readonly().default([
@@ -16766,6 +16887,66 @@ method(object({
16766
16887
  password: string()
16767
16888
  }), AuthResultSchema.nullable(), { kind: "mutation" }), method(object({ state: string() }), string()), method(record(string(), string()), AuthResultSchema, { kind: "mutation" }), method(object({ token: string() }), AuthResultSchema.nullable());
16768
16889
  /**
16890
+ * `login-method` — collection cap through which auth addons contribute
16891
+ * their pre-auth login surfaces to the login page. This is the SINGLE,
16892
+ * generic mechanism that supersedes the dead `auth.listProviders` reader:
16893
+ * every auth addon (OIDC, magic-link, WebAuthn/passkey) registers a
16894
+ * `login-method` provider and the PUBLIC `auth.listLoginMethods`
16895
+ * procedure aggregates them for the unauthenticated login page.
16896
+ *
16897
+ * A contribution is a discriminated union on `kind`:
16898
+ *
16899
+ * - `redirect` — a declarative button. The login page renders a generic
16900
+ * button that navigates to `startUrl` (an addon-owned HTTP route).
16901
+ * Covers OIDC (`/addon/auth-oidc/<id>/start`) and magic-link with
16902
+ * ZERO shell-side JS. A future SSO addon plugs in the same way — the
16903
+ * login page needs NO change.
16904
+ *
16905
+ * - `widget` — a Module-Federation widget the login page mounts (via
16906
+ * `loadRemoteBundle`) for an in-page ceremony. Covers the passkey
16907
+ * login ceremony, which must run `@simplewebauthn/browser` INSIDE the
16908
+ * addon bundle. The referenced widget also declares `preAuth: true` in
16909
+ * its `addon-widgets-source` catalog entry. `auth.listLoginMethods`
16910
+ * stamps a public `bundleUrl` from `addonId` + `bundle`.
16911
+ *
16912
+ * Every contribution carries a `stage`:
16913
+ * - `primary` — shown on the first credentials screen (OIDC /
16914
+ * magic-link buttons; a future usernameless passkey).
16915
+ * - `second-factor` — shown AFTER the password leg, gated on the
16916
+ * returned `factors` (passkey-as-2FA today).
16917
+ *
16918
+ * `mount: skip` — the cap is read server-side by the core auth router
16919
+ * (`registry.getCollection('login-method')`), never mounted as its own
16920
+ * tRPC router.
16921
+ */
16922
+ /** When a login method renders in the two-phase login flow. */
16923
+ var LoginStageEnum = _enum(["primary", "second-factor"]);
16924
+ /** One login-method contribution — redirect button OR pre-auth widget. */
16925
+ var LoginMethodContributionSchema = discriminatedUnion("kind", [object({
16926
+ kind: literal("redirect"),
16927
+ /** Stable id within the login-method set (e.g. `auth-oidc/google`). */
16928
+ id: string(),
16929
+ /** Operator-facing button label. */
16930
+ label: string(),
16931
+ /** lucide-react icon name. */
16932
+ icon: string().optional(),
16933
+ /** Addon-owned HTTP route the button navigates to (GET). */
16934
+ startUrl: string(),
16935
+ stage: LoginStageEnum
16936
+ }), object({
16937
+ kind: literal("widget"),
16938
+ /** Stable id within the login-method set (e.g. `auth-webauthn/passkey-login`). */
16939
+ id: string(),
16940
+ /** Owning addon id — drives the public bundle URL + the MF namespace. */
16941
+ addonId: string(),
16942
+ /** Bundle filename inside the addon's dist dir (`remoteEntry.js`). */
16943
+ bundle: string(),
16944
+ /** MF remote descriptor — `{ remoteName, exposedModule, componentKey }`. */
16945
+ remote: WidgetRemoteSchema,
16946
+ stage: LoginStageEnum
16947
+ })]);
16948
+ method(_void(), array(LoginMethodContributionSchema).readonly());
16949
+ /**
16769
16950
  * Orchestrator-side destination metadata. The orchestrator computes
16770
16951
  * `id = <addonId>:<subId>` from its provider lookup so consumers
16771
16952
  * (admin UI, restore flow) see one canonical key.
@@ -18869,7 +19050,17 @@ var TrackSchema = object({
18869
19050
  /** Cumulative normalized distance travelled (0..1 units = full frame width). */
18870
19051
  totalDistance: number(),
18871
19052
  state: TrackStateSchema,
18872
- active: boolean()
19053
+ active: boolean(),
19054
+ /** Deterministic key-event importance score in [0,1] (server-computed at
19055
+ * track expiry, recomputed on late label). Absent on legacy rows written
19056
+ * before scoring shipped — consumers degrade to absence / compute-on-read. */
19057
+ importance: number().optional(),
19058
+ /** Id of the track's highest-confidence ObjectEvent (its representative
19059
+ * "best" frame). Absent when the track produced no object events. */
19060
+ bestEventId: string().optional(),
19061
+ /** Tag of the importance sub-signal that dominated the score
19062
+ * (identity|dwell|proximity|class|confidence|travel|zone). */
19063
+ importanceReason: string().optional()
18873
19064
  });
18874
19065
  var BaseEventFields = {
18875
19066
  id: string(),
@@ -18934,8 +19125,18 @@ var ObjectEventSchema = object({
18934
19125
  frameHeight: number().optional(),
18935
19126
  /** MediaStore key for the crop attached to this event (if any). */
18936
19127
  mediaKey: string().optional(),
19128
+ /** Design B: MediaStore key of the track's native-resolution key frame (the
19129
+ * best-detection full frame). Resolve via the event-media data-plane
19130
+ * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
19131
+ * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
19132
+ * sources — consumers fall back to `mediaKey` (the tight crop). */
19133
+ keyFrameMediaKey: string().optional(),
18937
19134
  /** Populated by B5 (recording playback URL for this event). */
18938
- mediaUrl: string().optional()
19135
+ mediaUrl: string().optional(),
19136
+ /** The parent track's key-event importance [0,1], propagated to every object
19137
+ * event of the track (so an event row can be sorted by importance without a
19138
+ * track join). Absent on legacy rows / before the track was scored. */
19139
+ importance: number().optional()
18939
19140
  });
18940
19141
  var AudioEventSchema = object({
18941
19142
  ...BaseEventFields,
@@ -18959,7 +19160,8 @@ var MediaFileKindEnum = _enum([
18959
19160
  "fullFrame",
18960
19161
  "fullFrameBoxed",
18961
19162
  "faceCrop",
18962
- "plateCrop"
19163
+ "plateCrop",
19164
+ "keyFrame"
18963
19165
  ]);
18964
19166
  var MediaFileSchema = object({
18965
19167
  key: string(),
@@ -18980,6 +19182,32 @@ var DeviceEventQueryInput = object({
18980
19182
  projection: _enum(["full", "slim"]).optional()
18981
19183
  });
18982
19184
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
19185
+ var KeyEventQueryInput = object({
19186
+ deviceId: number(),
19187
+ /** Window lower bound (track firstSeen ≥ since). */
19188
+ since: number(),
19189
+ /** Window upper bound (track firstSeen ≤ until). */
19190
+ until: number(),
19191
+ limit: number().int().min(1).max(200).default(50),
19192
+ /** Drop tracks scoring below this importance. */
19193
+ minImportance: number().min(0).max(1).optional(),
19194
+ /** Restrict to a single class (e.g. 'person'). */
19195
+ classFilter: string().optional()
19196
+ });
19197
+ var KeyEventSchema = object({
19198
+ /** The representative event id (the track's best ObjectEvent, else its trackId). */
19199
+ id: string(),
19200
+ trackId: string(),
19201
+ /** Track start time (firstSeen). */
19202
+ timestamp: number(),
19203
+ className: string(),
19204
+ label: string().optional(),
19205
+ importance: number(),
19206
+ /** Highest-confidence ObjectEvent id for the track (empty when none). */
19207
+ bestEventId: string(),
19208
+ /** Track lifetime in ms (lastSeen - firstSeen). */
19209
+ windowMs: number().optional()
19210
+ });
18983
19211
  var TrackedDetectionSchema = object({
18984
19212
  trackId: string(),
18985
19213
  className: string(),
@@ -19009,7 +19237,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19009
19237
  }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
19010
19238
  kind: "mutation",
19011
19239
  auth: "admin"
19012
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({
19240
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19013
19241
  deviceId: number(),
19014
19242
  since: number(),
19015
19243
  until: number(),
@@ -19054,11 +19282,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19054
19282
  timestamp: number()
19055
19283
  });
19056
19284
  var CameraPipelineConfigSchema = object({
19057
- engine: PipelineEngineChoiceSchema,
19285
+ engine: PipelineEngineChoiceSchema.optional(),
19058
19286
  steps: array(PipelineStepInputSchema).readonly(),
19059
19287
  audio: object({
19060
- engine: PipelineEngineChoiceSchema,
19061
- modelId: string(),
19288
+ engine: PipelineEngineChoiceSchema.optional(),
19289
+ modelId: string().optional(),
19062
19290
  enabled: boolean(),
19063
19291
  settings: record(string(), unknown()).readonly().optional()
19064
19292
  }).nullable().optional()
@@ -19073,7 +19301,7 @@ var PipelineTemplateSchema = object({
19073
19301
  });
19074
19302
  var AgentAddonConfigSchema = object({
19075
19303
  enabled: boolean(),
19076
- modelId: string(),
19304
+ modelId: string().optional(),
19077
19305
  settings: record(string(), unknown()).readonly()
19078
19306
  });
19079
19307
  var AgentPipelineSettingsSchema = object({
@@ -19083,12 +19311,25 @@ var AgentPipelineSettingsSchema = object({
19083
19311
  detectWeight: number().positive().optional(),
19084
19312
  /** Node is eligible to run the detection pipeline (decode + inference). */
19085
19313
  detect: boolean().optional(),
19086
- /** Node is eligible to host decoder sessions. */
19314
+ /**
19315
+ * DEPRECATED AND IGNORED. Decode is always co-located with its frame
19316
+ * consumer, so decode eligibility IS detect eligibility. Kept optional in
19317
+ * the schema ONLY so persisted stores written before the removal still
19318
+ * parse — no code reads it and no write path emits it.
19319
+ */
19087
19320
  decode: boolean().optional(),
19088
19321
  /** Node is eligible to run audio-analyzer sessions. */
19089
19322
  audio: boolean().optional(),
19090
19323
  /** Node is eligible to be the ingest / source-owner (serve the restream). */
19091
- ingest: boolean().optional()
19324
+ ingest: boolean().optional(),
19325
+ /**
19326
+ * Operator override for the LAN host a cross-node decoder dials to reach
19327
+ * THIS node's restream (Cluster UI). Absent → auto-detect: a remote runner
19328
+ * falls back to its `CAMSTACK_HUB_URL`-derived host (the Moleculer address
19329
+ * it already uses to reach the hub). Set this only when the auto-detected
19330
+ * address is wrong (multi-homed host, NAT, custom interface).
19331
+ */
19332
+ reachableHost: string().optional()
19092
19333
  });
19093
19334
  var CameraPipelineForAgentSchema = object({
19094
19335
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19136,25 +19377,6 @@ var PipelineAssignmentSchema = object({
19136
19377
  assignedAt: number()
19137
19378
  });
19138
19379
  /**
19139
- * Decoder placement record. Symmetric to `PipelineAssignmentSchema` but for
19140
- * the decoder-node placement domain (`balanceDecoder` decision: manual pin
19141
- * → co-located with pipeline → capacity).
19142
- */
19143
- var DecoderAssignmentSchema = object({
19144
- deviceId: number(),
19145
- /** Moleculer node id of the decoder provider currently responsible for this camera. */
19146
- decoderNodeId: string(),
19147
- /** True when the assignment was set manually via `assignDecoder`, false when chosen by the balancer. */
19148
- pinned: boolean(),
19149
- /** Why this assignment was made — useful for debugging the decoder balancer. */
19150
- reason: _enum([
19151
- "manual",
19152
- "co-located",
19153
- "capacity",
19154
- "hardware-affinity"
19155
- ])
19156
- });
19157
- /**
19158
19380
  * Per-agent load summary surfaced to the load balancer + dashboards.
19159
19381
  * Aggregated from each runner's `getLocalLoad` cap call.
19160
19382
  */
@@ -19194,6 +19416,15 @@ var GlobalMetricsSchema = object({
19194
19416
  * capability providers.
19195
19417
  */
19196
19418
  var CapabilityBindingsSchema = record(string(), string());
19419
+ /**
19420
+ * The cluster's single camera-source owner (`clusterRoles.ingestNode`) plus
19421
+ * its LAN-reachable host, if one is registered. See `getIngestOwner`.
19422
+ */
19423
+ var IngestOwnerSchema = object({
19424
+ ownerNodeId: string(),
19425
+ reachableHost: string().optional(),
19426
+ configIssue: string().optional()
19427
+ });
19197
19428
  /** Source block — always present; derives from the stream catalog. */
19198
19429
  var CameraSourceStatusSchema = object({ streams: array(object({
19199
19430
  camStreamId: string(),
@@ -19208,6 +19439,14 @@ var CameraAssignmentStatusSchema = object({
19208
19439
  detectionNodeId: string().nullable(),
19209
19440
  decoderNodeId: string().nullable(),
19210
19441
  audioNodeId: string().nullable(),
19442
+ /**
19443
+ * The node that OWNS this camera's physical source pull (dials the RTSP and
19444
+ * hosts the broker/restream) — the cluster ingest owner today
19445
+ * (`clusterRoles.ingestNode`), per-camera once source assignment lands. Lets
19446
+ * the UI show WHERE a camera is sourced without SSH/logs, and is the node the
19447
+ * broker block below was read from (pinned). Nullable only pre-wiring.
19448
+ */
19449
+ sourceNodeId: string().nullable(),
19211
19450
  pinned: object({
19212
19451
  detection: boolean(),
19213
19452
  decoder: boolean(),
@@ -19340,16 +19579,7 @@ method(object({
19340
19579
  }), object({ success: literal(true) }), {
19341
19580
  kind: "mutation",
19342
19581
  auth: "admin"
19343
- }), method(object({
19344
- deviceId: number(),
19345
- nodeId: string()
19346
- }), _void(), {
19347
- kind: "mutation",
19348
- auth: "admin"
19349
- }), method(object({ deviceId: number() }), _void(), {
19350
- kind: "mutation",
19351
- auth: "admin"
19352
- }), method(_void(), array(DecoderAssignmentSchema).readonly()), method(object({
19582
+ }), method(_void(), IngestOwnerSchema), method(object({
19353
19583
  deviceId: number(),
19354
19584
  nodeId: string()
19355
19585
  }), object({ success: literal(true) }), {
@@ -19370,10 +19600,7 @@ method(object({
19370
19600
  nodeId: string(),
19371
19601
  pinned: boolean(),
19372
19602
  assignedAt: number()
19373
- }))), method(object({
19374
- deviceId: number(),
19375
- pipelineNodeId: string().optional()
19376
- }), DecoderAssignmentSchema), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
19603
+ }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
19377
19604
  nodeId: string(),
19378
19605
  settings: AgentPipelineSettingsSchema
19379
19606
  })).readonly()), method(object({
@@ -19403,12 +19630,26 @@ method(object({
19403
19630
  }), method(object({
19404
19631
  agentNodeId: string(),
19405
19632
  detect: boolean().nullable().optional(),
19406
- decode: boolean().nullable().optional(),
19407
19633
  audio: boolean().nullable().optional(),
19408
19634
  ingest: boolean().nullable().optional()
19409
19635
  }), object({ success: literal(true) }), {
19410
19636
  kind: "mutation",
19411
19637
  auth: "admin"
19638
+ }), method(object({
19639
+ agentNodeId: string(),
19640
+ reachableHost: string().nullable()
19641
+ }), object({ success: literal(true) }), {
19642
+ kind: "mutation",
19643
+ auth: "admin"
19644
+ }), method(object({ agentNodeId: string() }), object({
19645
+ success: literal(true),
19646
+ /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
19647
+ effectiveModelId: string().nullable(),
19648
+ /** Number of cameras whose node-scoped overrides were cleared. */
19649
+ clearedCameraOverrides: number()
19650
+ }), {
19651
+ kind: "mutation",
19652
+ auth: "admin"
19412
19653
  }), method(object({ deviceId: number() }), CameraPipelineSettingsSchema.nullable()), method(object({
19413
19654
  deviceId: number(),
19414
19655
  addonId: string(),
@@ -19453,22 +19694,131 @@ method(object({
19453
19694
  kind: "mutation",
19454
19695
  auth: "admin"
19455
19696
  });
19456
- var RegisteredStreamSchema = object({
19457
- streamId: string(),
19458
- label: string().optional(),
19459
- codec: string(),
19460
- type: _enum(["video", "audio"]),
19461
- sourceUrl: string()
19697
+ /**
19698
+ * server-management — per-NODE singleton capability for a node's ROOT
19699
+ * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
19700
+ * agents).
19701
+ *
19702
+ * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
19703
+ * on agents) carries the whole software stack in its npm dep tree, so ONE
19704
+ * version describes the node. Updates install into
19705
+ * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
19706
+ * starter (probation boot + auto-rollback to N-1).
19707
+ *
19708
+ * Providers:
19709
+ * - HUB: `ServerUpdateService` behind the `server-provided` mount
19710
+ * (`buildServerProviders` in trpc.router.ts) — the default target for
19711
+ * unpinned calls.
19712
+ * - AGENT: `AgentUpdateService` registered by the agent bootstrap under
19713
+ * the synthetic `agent-runtime` addonId and declared in the agent's
19714
+ * `$hub.registerNode` manifest.
19715
+ *
19716
+ * Node routing: singleton caps get the codegen/runtime-builder `nodeId`
19717
+ * injection on every method — `input.nodeId` (or `nodePin(nodeId)` from the
19718
+ * SDK) routes the call to that node's provider via the standard remote
19719
+ * proxy (`createCapabilityProxy` → `$agent-cap-fwd` → the agent's
19720
+ * in-process provider lookup). No `nodeId` → the hub's own provider.
19721
+ *
19722
+ * Spec: docs/superpowers/specs/2026-07-12-runtime-updatable-node-packages-design.md
19723
+ */
19724
+ /**
19725
+ * Where the running hub's code was loaded from:
19726
+ * - `workspace` — dev checkout (tsx / workspace dist); the starter defers to
19727
+ * plain resolution and runtime updates are refused.
19728
+ * - `baked` — the immutable image seed closure (no data-dir root active).
19729
+ * - `data-root` — the runtime-updatable `<dataDir>/server-root` closure.
19730
+ */
19731
+ var ServerBootModeSchema = _enum([
19732
+ "workspace",
19733
+ "baked",
19734
+ "data-root"
19735
+ ]);
19736
+ /**
19737
+ * Update lifecycle state:
19738
+ * - `idle` / `checking` / `staging` — steady / in-flight registry work.
19739
+ * - `pending-restart` — a version is staged and the node has NOT yet
19740
+ * restarted onto it (still running the OLD version).
19741
+ * - `awaiting-confirmation` — the node HAS restarted onto the staged version
19742
+ * (it is the active probation boot) and is waiting to confirm boot-health.
19743
+ * Apply/rollback are refused in this state and the node must NOT be
19744
+ * manually restarted, or the probation boot auto-rolls-back.
19745
+ */
19746
+ var ServerUpdateStateSchema = _enum([
19747
+ "idle",
19748
+ "checking",
19749
+ "staging",
19750
+ "pending-restart",
19751
+ "awaiting-confirmation"
19752
+ ]);
19753
+ var ServerRollbackInfoSchema = object({
19754
+ /** The version that failed (or was manually rolled back). */
19755
+ fromVersion: string(),
19756
+ /** The version rolled back to; null = the baked seed. */
19757
+ toVersion: string().nullable(),
19758
+ atMs: number(),
19759
+ reason: string()
19462
19760
  });
19463
- var ExposedResourceSchema = object({
19464
- streamId: string(),
19465
- format: string(),
19466
- value: string()
19761
+ var ServerPackageStatusSchema = object({
19762
+ /** Root package name (`@camstack/server` on the hub). */
19763
+ packageName: string(),
19764
+ /** Version of the code the running process ACTUALLY loaded. */
19765
+ runningVersion: string().nullable(),
19766
+ /** Node.js runtime version the node's process runs on (`process.versions.node`). */
19767
+ nodeRuntimeVersion: string().nullable(),
19768
+ /** Active data-dir root version; null when booted from seed/workspace. */
19769
+ activeVersion: string().nullable(),
19770
+ /** N-1 version kept for rollback; null when no previous version exists. */
19771
+ previousVersion: string().nullable(),
19772
+ /** Version of the immutable baked seed closure (image fallback). */
19773
+ seedVersion: string().nullable(),
19774
+ /** Latest registry version from the most recent check (null = never checked). */
19775
+ latestVersion: string().nullable(),
19776
+ updateAvailable: boolean(),
19777
+ bootMode: ServerBootModeSchema,
19778
+ updateState: ServerUpdateStateSchema,
19779
+ /** Version staged + awaiting its probation boot, when one is pending. */
19780
+ pendingVersion: string().nullable(),
19781
+ /** Set when the last freshly-activated version failed its boot health-check. */
19782
+ rolledBack: ServerRollbackInfoSchema.nullable(),
19783
+ /**
19784
+ * True when `server-root/state.json` EXISTS but is unreadable/corrupt — the
19785
+ * hub is running from the baked seed (or workspace) while installed data-dir
19786
+ * versions are being IGNORED. Surfaced as a warning in the UI.
19787
+ */
19788
+ stateFileCorrupt: boolean(),
19789
+ lastCheckedAtMs: number().nullable()
19790
+ });
19791
+ var ServerUpdateCheckResultSchema = object({
19792
+ packageName: string(),
19793
+ runningVersion: string().nullable(),
19794
+ latestVersion: string().nullable(),
19795
+ updateAvailable: boolean(),
19796
+ checkedAtMs: number(),
19797
+ /** Non-null when the registry lookup failed (offline, bad registry, …). */
19798
+ error: string().nullable()
19799
+ });
19800
+ var ServerUpdateActionResultSchema = object({
19801
+ accepted: boolean(),
19802
+ targetVersion: string().nullable(),
19803
+ /** True when a graceful restart was scheduled to apply the change. */
19804
+ restarting: boolean(),
19805
+ message: string()
19806
+ });
19807
+ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
19808
+ kind: "mutation",
19809
+ auth: "admin"
19810
+ }), method(object({
19811
+ /** Explicit target version; omitted = latest from the registry. */
19812
+ version: string().optional() }), ServerUpdateActionResultSchema, {
19813
+ kind: "mutation",
19814
+ auth: "admin"
19815
+ }), method(_void(), ServerUpdateActionResultSchema, {
19816
+ kind: "mutation",
19817
+ auth: "admin"
19818
+ }), method(_void(), ServerUpdateActionResultSchema, {
19819
+ kind: "mutation",
19820
+ auth: "admin"
19467
19821
  });
19468
- method(object({
19469
- deviceId: number(),
19470
- streams: array(RegisteredStreamSchema).readonly()
19471
- }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), array(ExposedResourceSchema).readonly());
19472
19822
  /**
19473
19823
  * Query filter for settings-store collections.
19474
19824
  */
@@ -19621,9 +19971,9 @@ method(SendEmailInputSchema, SendEmailResultSchema, {
19621
19971
  /**
19622
19972
  * A single device snapshot returned as base64 JPEG/PNG.
19623
19973
  *
19624
- * Shared with the `snapshot-provider` collection cap the orchestrator
19625
- * receives the same shape from each native provider and from the
19626
- * broker-based fallback.
19974
+ * The `SnapshotAddon` wrapper returns this shape whether the frame came from
19975
+ * the device-native provider (onboard capture) or from the stream-broker
19976
+ * prebuffer fallback.
19627
19977
  */
19628
19978
  var SnapshotImageSchema = object({
19629
19979
  base64: string(),
@@ -19654,11 +20004,12 @@ DeviceType.Camera, method(object({
19654
20004
  }), SnapshotImageSchema.nullable()), method(object({ deviceId: number() }), _void(), {
19655
20005
  kind: "mutation",
19656
20006
  auth: "admin"
19657
- });
19658
- method(object({ deviceId: number() }), boolean()), method(object({
20007
+ }), systemMethod(object({ deviceIds: array(number()).min(1).max(200) }), array(object({
19659
20008
  deviceId: number(),
19660
- streamId: string().optional()
19661
- }), SnapshotImageSchema.nullable());
20009
+ lastCapturedAt: number().nullable(),
20010
+ cacheAgeMs: number().nullable(),
20011
+ etag: string().nullable()
20012
+ })));
19662
20013
  /**
19663
20014
  * `sso-bridge` — internal hub-only cap that lets SSO-style auth
19664
20015
  * providers (OIDC, SAML, magic-link, …) mint an HMAC-signed token
@@ -19909,10 +20260,32 @@ method(_void(), array(TurnServerSchema).readonly());
19909
20260
  * b. `finishAuthentication({userId, response})` → server verifies
19910
20261
  * the assertion, bumps the credential counter, returns ok.
19911
20262
  *
20263
+ * 2b. Usernameless (discoverable-credential) authentication — the
20264
+ * passkey IS the primary factor, no password leg:
20265
+ * a. `beginDiscoverableAuthentication({})` → assertion options with
20266
+ * EMPTY `allowCredentials` (the browser offers every resident
20267
+ * passkey it holds for this RP) + `userVerification: 'required'`
20268
+ * (the passkey replaces both factors, so UV is mandatory).
20269
+ * The challenge is stored server-side, NOT bound to any user.
20270
+ * b. `finishDiscoverableAuthentication({response})` → the provider
20271
+ * resolves the credential by the response's credential id,
20272
+ * verifies the assertion against the stored challenge + that
20273
+ * credential's public key/counter, and returns the OWNING
20274
+ * `userId` — the caller (core auth router) mints the session.
20275
+ *
19912
20276
  * 3. Management:
19913
20277
  * - `listPasskeys({userId})` — enumerate user's enrolled credentials.
19914
20278
  * - `removePasskey({userId, credentialId})` — revoke one credential.
19915
20279
  *
20280
+ * 4. Second-factor preference (opt-in, default OFF):
20281
+ * Enrolling a passkey only enables passkey-FIRST sign-in. It is
20282
+ * demanded as a second factor after a password login ONLY when the
20283
+ * user explicitly opts in via `setSecondFactorPreference`.
20284
+ * - `getSecondFactorPreference({userId})` → `{ enabled }` (missing
20285
+ * row ⇒ `enabled: false`).
20286
+ * - `setSecondFactorPreference({userId, enabled})` — persisted by
20287
+ * the providing addon beside its credentials.
20288
+ *
19916
20289
  * Challenges are short-lived (5 min, in-memory). The cap is internal —
19917
20290
  * the admin-ui composes the begin/finish round-trip and never exposes
19918
20291
  * the cap to non-admins.
@@ -19955,6 +20328,17 @@ method(object({
19955
20328
  }), object({ verified: boolean() }), {
19956
20329
  kind: "mutation",
19957
20330
  access: "view"
20331
+ }), method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
20332
+ kind: "mutation",
20333
+ access: "view"
20334
+ }), method(object({
20335
+ /** AuthenticationResponseJSON from the browser. */
20336
+ response: record(string(), unknown()) }), object({
20337
+ verified: boolean(),
20338
+ userId: string().nullable()
20339
+ }), {
20340
+ kind: "mutation",
20341
+ access: "view"
19958
20342
  }), method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }), method(object({
19959
20343
  userId: string(),
19960
20344
  credentialId: string()
@@ -19962,6 +20346,13 @@ method(object({
19962
20346
  kind: "mutation",
19963
20347
  auth: "admin",
19964
20348
  access: "delete"
20349
+ }), method(object({ userId: string() }), object({ enabled: boolean() }), { auth: "admin" }), method(object({
20350
+ userId: string(),
20351
+ enabled: boolean()
20352
+ }), object({ success: literal(true) }), {
20353
+ kind: "mutation",
20354
+ auth: "admin",
20355
+ access: "create"
19965
20356
  });
19966
20357
  /**
19967
20358
  * `videoclips` — the unified, navigable-clip surface for a camera.
@@ -20019,9 +20410,10 @@ method(object({
20019
20410
  auth: "admin"
20020
20411
  });
20021
20412
  /**
20022
- * Optional client-side hints sent at session creation to help the
20023
- * provider pick the best native source. All fields are optional —
20024
- * a viewer that knows nothing still gets a sane default.
20413
+ * Optional client-side hints sent at session creation to help the provider
20414
+ * pick the best native source. All fields optional — a viewer that knows
20415
+ * nothing still gets a sane default. (Relocated from the retired `webrtc`
20416
+ * collection cap; this `webrtc-session` cap is the live signaling surface.)
20025
20417
  */
20026
20418
  var webrtcClientHintsSchema = object({
20027
20419
  viewportWidth: number().int().positive().optional(),
@@ -20032,22 +20424,6 @@ var webrtcClientHintsSchema = object({
20032
20424
  /** Hard tier override; takes precedence over scoring when registered. */
20033
20425
  prefersTier: string().optional()
20034
20426
  }).partial();
20035
- method(object({
20036
- streamId: string(),
20037
- sdpOffer: string()
20038
- }), string(), { kind: "mutation" }), method(object({ streamId: string() }), boolean()), method(object({
20039
- streamId: string(),
20040
- codec: string()
20041
- }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), _void(), { kind: "mutation" }), method(object({
20042
- streamId: string(),
20043
- hints: webrtcClientHintsSchema.optional()
20044
- }), object({
20045
- sessionId: string(),
20046
- sdpOffer: string()
20047
- }), { kind: "mutation" }), method(object({
20048
- sessionId: string(),
20049
- sdpAnswer: string()
20050
- }), _void(), { kind: "mutation" }), method(object({ sessionId: string() }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), boolean());
20051
20427
  /**
20052
20428
  * Discriminated target for a WebRTC session. The client sends this
20053
20429
  * structured object instead of building / parsing brokerId strings;
@@ -20778,7 +21154,17 @@ var FaceInfoSchema = object({
20778
21154
  recognizedIdentityId: string().optional(),
20779
21155
  identityName: string().optional(),
20780
21156
  assigned: boolean(),
20781
- base64: string().optional()
21157
+ base64: string().optional(),
21158
+ /** Design B: the face bbox (pixel space) on the key frame — lets a detail
21159
+ * view draw the box over the native `keyFrameMediaKey` frame. Absent on
21160
+ * legacy rows written before design B. */
21161
+ faceBbox: BoundingBoxSchema.optional(),
21162
+ /** Design B: MediaStore key of the track's native-resolution key frame.
21163
+ * Fetch the native JPEG via the event-media data-plane
21164
+ * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
21165
+ * track produced no key frame (e.g. native/onboard source) — the UI falls
21166
+ * back to the inline `base64` face crop. */
21167
+ keyFrameMediaKey: string().optional()
20782
21168
  });
20783
21169
  var FaceFilterEnum = _enum([
20784
21170
  "unassigned",
@@ -21475,6 +21861,16 @@ var TopologyCategorySchema = object({
21475
21861
  healthy: number(),
21476
21862
  addons: array(TopologyCategoryAddonSchema).readonly()
21477
21863
  });
21864
+ /**
21865
+ * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
21866
+ * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
21867
+ * version visibility for the Server management surface. Nullable: offline
21868
+ * rows and pre-phase-2 nodes report none.
21869
+ */
21870
+ var TopologyRootPackageSchema = object({
21871
+ name: string(),
21872
+ version: string()
21873
+ });
21478
21874
  var TopologyNodeSchema = object({
21479
21875
  id: string(),
21480
21876
  name: string(),
@@ -21498,7 +21894,8 @@ var TopologyNodeSchema = object({
21498
21894
  status: string()
21499
21895
  })).readonly(),
21500
21896
  processes: array(TopologyProcessSchema).readonly(),
21501
- categories: array(TopologyCategorySchema).readonly()
21897
+ categories: array(TopologyCategorySchema).readonly(),
21898
+ rootPackage: TopologyRootPackageSchema.nullable()
21502
21899
  });
21503
21900
  var CapUsageEdgeSchema = object({
21504
21901
  callerAddonId: string(),
@@ -24298,6 +24695,12 @@ Object.freeze({
24298
24695
  addonId: null,
24299
24696
  access: "create"
24300
24697
  },
24698
+ "loginMethod.getLoginMethods": {
24699
+ capName: "login-method",
24700
+ capScope: "system",
24701
+ addonId: null,
24702
+ access: "view"
24703
+ },
24301
24704
  "mediaPlayer.next": {
24302
24705
  capName: "media-player",
24303
24706
  capScope: "device",
@@ -24880,6 +25283,12 @@ Object.freeze({
24880
25283
  addonId: null,
24881
25284
  access: "view"
24882
25285
  },
25286
+ "pipelineAnalytics.getKeyEvents": {
25287
+ capName: "pipeline-analytics",
25288
+ capScope: "device",
25289
+ addonId: null,
25290
+ access: "view"
25291
+ },
24883
25292
  "pipelineAnalytics.getMotionEvents": {
24884
25293
  capName: "pipeline-analytics",
24885
25294
  capScope: "device",
@@ -24928,23 +25337,23 @@ Object.freeze({
24928
25337
  addonId: null,
24929
25338
  access: "create"
24930
25339
  },
24931
- "pipelineExecutor.deleteModel": {
25340
+ "pipelineExecutor.clearDeviceOverrides": {
24932
25341
  capName: "pipeline-executor",
24933
25342
  capScope: "system",
24934
25343
  addonId: null,
24935
25344
  access: "delete"
24936
25345
  },
24937
- "pipelineExecutor.deleteTemplate": {
25346
+ "pipelineExecutor.deleteModel": {
24938
25347
  capName: "pipeline-executor",
24939
25348
  capScope: "system",
24940
25349
  addonId: null,
24941
25350
  access: "delete"
24942
25351
  },
24943
- "pipelineExecutor.detect": {
25352
+ "pipelineExecutor.deleteTemplate": {
24944
25353
  capName: "pipeline-executor",
24945
25354
  capScope: "system",
24946
25355
  addonId: null,
24947
- access: "view"
25356
+ access: "delete"
24948
25357
  },
24949
25358
  "pipelineExecutor.downloadModel": {
24950
25359
  capName: "pipeline-executor",
@@ -25138,13 +25547,13 @@ Object.freeze({
25138
25547
  addonId: null,
25139
25548
  access: "create"
25140
25549
  },
25141
- "pipelineOrchestrator.assignAudio": {
25142
- capName: "pipeline-orchestrator",
25550
+ "pipelineExecutor.validatePipeline": {
25551
+ capName: "pipeline-executor",
25143
25552
  capScope: "system",
25144
25553
  addonId: null,
25145
- access: "create"
25554
+ access: "view"
25146
25555
  },
25147
- "pipelineOrchestrator.assignDecoder": {
25556
+ "pipelineOrchestrator.assignAudio": {
25148
25557
  capName: "pipeline-orchestrator",
25149
25558
  capScope: "system",
25150
25559
  addonId: null,
@@ -25228,19 +25637,13 @@ Object.freeze({
25228
25637
  addonId: null,
25229
25638
  access: "view"
25230
25639
  },
25231
- "pipelineOrchestrator.getDecoderAssignment": {
25232
- capName: "pipeline-orchestrator",
25233
- capScope: "system",
25234
- addonId: null,
25235
- access: "view"
25236
- },
25237
- "pipelineOrchestrator.getDecoderAssignments": {
25640
+ "pipelineOrchestrator.getGlobalMetrics": {
25238
25641
  capName: "pipeline-orchestrator",
25239
25642
  capScope: "system",
25240
25643
  addonId: null,
25241
25644
  access: "view"
25242
25645
  },
25243
- "pipelineOrchestrator.getGlobalMetrics": {
25646
+ "pipelineOrchestrator.getIngestOwner": {
25244
25647
  capName: "pipeline-orchestrator",
25245
25648
  capScope: "system",
25246
25649
  addonId: null,
@@ -25282,6 +25685,12 @@ Object.freeze({
25282
25685
  addonId: null,
25283
25686
  access: "delete"
25284
25687
  },
25688
+ "pipelineOrchestrator.resetNodePipelineDefaults": {
25689
+ capName: "pipeline-orchestrator",
25690
+ capScope: "system",
25691
+ addonId: null,
25692
+ access: "delete"
25693
+ },
25285
25694
  "pipelineOrchestrator.resolvePipeline": {
25286
25695
  capName: "pipeline-orchestrator",
25287
25696
  capScope: "system",
@@ -25318,37 +25727,37 @@ Object.freeze({
25318
25727
  addonId: null,
25319
25728
  access: "create"
25320
25729
  },
25321
- "pipelineOrchestrator.setCameraPipelineForAgent": {
25730
+ "pipelineOrchestrator.setAgentReachableHost": {
25322
25731
  capName: "pipeline-orchestrator",
25323
25732
  capScope: "system",
25324
25733
  addonId: null,
25325
25734
  access: "create"
25326
25735
  },
25327
- "pipelineOrchestrator.setCameraStepOverride": {
25736
+ "pipelineOrchestrator.setCameraPipelineForAgent": {
25328
25737
  capName: "pipeline-orchestrator",
25329
25738
  capScope: "system",
25330
25739
  addonId: null,
25331
25740
  access: "create"
25332
25741
  },
25333
- "pipelineOrchestrator.setCameraStepToggle": {
25742
+ "pipelineOrchestrator.setCameraStepOverride": {
25334
25743
  capName: "pipeline-orchestrator",
25335
25744
  capScope: "system",
25336
25745
  addonId: null,
25337
25746
  access: "create"
25338
25747
  },
25339
- "pipelineOrchestrator.setCapabilityBinding": {
25748
+ "pipelineOrchestrator.setCameraStepToggle": {
25340
25749
  capName: "pipeline-orchestrator",
25341
25750
  capScope: "system",
25342
25751
  addonId: null,
25343
25752
  access: "create"
25344
25753
  },
25345
- "pipelineOrchestrator.unassignAudio": {
25754
+ "pipelineOrchestrator.setCapabilityBinding": {
25346
25755
  capName: "pipeline-orchestrator",
25347
25756
  capScope: "system",
25348
25757
  addonId: null,
25349
25758
  access: "create"
25350
25759
  },
25351
- "pipelineOrchestrator.unassignDecoder": {
25760
+ "pipelineOrchestrator.unassignAudio": {
25352
25761
  capName: "pipeline-orchestrator",
25353
25762
  capScope: "system",
25354
25763
  addonId: null,
@@ -25408,6 +25817,12 @@ Object.freeze({
25408
25817
  addonId: null,
25409
25818
  access: "view"
25410
25819
  },
25820
+ "pipelineRunner.getNativeCrop": {
25821
+ capName: "pipeline-runner",
25822
+ capScope: "system",
25823
+ addonId: null,
25824
+ access: "view"
25825
+ },
25411
25826
  "pipelineRunner.reportMotion": {
25412
25827
  capName: "pipeline-runner",
25413
25828
  capScope: "system",
@@ -25648,33 +26063,45 @@ Object.freeze({
25648
26063
  addonId: null,
25649
26064
  access: "create"
25650
26065
  },
25651
- "restreamer.getExposedResources": {
25652
- capName: "restreamer",
26066
+ "scriptRunner.run": {
26067
+ capName: "script-runner",
26068
+ capScope: "device",
26069
+ addonId: null,
26070
+ access: "create"
26071
+ },
26072
+ "scriptRunner.stop": {
26073
+ capName: "script-runner",
26074
+ capScope: "device",
26075
+ addonId: null,
26076
+ access: "create"
26077
+ },
26078
+ "serverManagement.applyServerUpdate": {
26079
+ capName: "server-management",
25653
26080
  capScope: "system",
25654
26081
  addonId: null,
25655
- access: "view"
26082
+ access: "create"
25656
26083
  },
25657
- "restreamer.registerDevice": {
25658
- capName: "restreamer",
26084
+ "serverManagement.checkServerUpdate": {
26085
+ capName: "server-management",
25659
26086
  capScope: "system",
25660
26087
  addonId: null,
25661
26088
  access: "create"
25662
26089
  },
25663
- "restreamer.unregisterDevice": {
25664
- capName: "restreamer",
26090
+ "serverManagement.getServerPackageStatus": {
26091
+ capName: "server-management",
25665
26092
  capScope: "system",
25666
26093
  addonId: null,
25667
- access: "delete"
26094
+ access: "view"
25668
26095
  },
25669
- "scriptRunner.run": {
25670
- capName: "script-runner",
25671
- capScope: "device",
26096
+ "serverManagement.restartServer": {
26097
+ capName: "server-management",
26098
+ capScope: "system",
25672
26099
  addonId: null,
25673
26100
  access: "create"
25674
26101
  },
25675
- "scriptRunner.stop": {
25676
- capName: "script-runner",
25677
- capScope: "device",
26102
+ "serverManagement.rollbackServerUpdate": {
26103
+ capName: "server-management",
26104
+ capScope: "system",
25678
26105
  addonId: null,
25679
26106
  access: "create"
25680
26107
  },
@@ -25762,23 +26189,17 @@ Object.freeze({
25762
26189
  addonId: null,
25763
26190
  access: "view"
25764
26191
  },
25765
- "snapshot.invalidateCache": {
26192
+ "snapshot.getSnapshotOverview": {
25766
26193
  capName: "snapshot",
25767
26194
  capScope: "device",
25768
26195
  addonId: null,
25769
- access: "create"
25770
- },
25771
- "snapshotProvider.getSnapshot": {
25772
- capName: "snapshot-provider",
25773
- capScope: "system",
25774
- addonId: null,
25775
26196
  access: "view"
25776
26197
  },
25777
- "snapshotProvider.supportsDevice": {
25778
- capName: "snapshot-provider",
25779
- capScope: "system",
26198
+ "snapshot.invalidateCache": {
26199
+ capName: "snapshot",
26200
+ capScope: "device",
25780
26201
  addonId: null,
25781
- access: "view"
26202
+ access: "create"
25782
26203
  },
25783
26204
  "ssoBridge.signBridgeToken": {
25784
26205
  capName: "sso-bridge",
@@ -26206,30 +26627,6 @@ Object.freeze({
26206
26627
  addonId: null,
26207
26628
  access: "view"
26208
26629
  },
26209
- "streamingEngine.getStreamUrl": {
26210
- capName: "streaming-engine",
26211
- capScope: "system",
26212
- addonId: null,
26213
- access: "view"
26214
- },
26215
- "streamingEngine.listStreams": {
26216
- capName: "streaming-engine",
26217
- capScope: "system",
26218
- addonId: null,
26219
- access: "view"
26220
- },
26221
- "streamingEngine.registerStream": {
26222
- capName: "streaming-engine",
26223
- capScope: "system",
26224
- addonId: null,
26225
- access: "create"
26226
- },
26227
- "streamingEngine.unregisterStream": {
26228
- capName: "streaming-engine",
26229
- capScope: "system",
26230
- addonId: null,
26231
- access: "delete"
26232
- },
26233
26630
  "streamParams.getConfigSchema": {
26234
26631
  capName: "stream-params",
26235
26632
  capScope: "device",
@@ -26476,6 +26873,12 @@ Object.freeze({
26476
26873
  addonId: null,
26477
26874
  access: "view"
26478
26875
  },
26876
+ "userPasskeys.beginDiscoverableAuthentication": {
26877
+ capName: "user-passkeys",
26878
+ capScope: "system",
26879
+ addonId: null,
26880
+ access: "view"
26881
+ },
26479
26882
  "userPasskeys.beginRegistration": {
26480
26883
  capName: "user-passkeys",
26481
26884
  capScope: "system",
@@ -26488,12 +26891,24 @@ Object.freeze({
26488
26891
  addonId: null,
26489
26892
  access: "view"
26490
26893
  },
26894
+ "userPasskeys.finishDiscoverableAuthentication": {
26895
+ capName: "user-passkeys",
26896
+ capScope: "system",
26897
+ addonId: null,
26898
+ access: "view"
26899
+ },
26491
26900
  "userPasskeys.finishRegistration": {
26492
26901
  capName: "user-passkeys",
26493
26902
  capScope: "system",
26494
26903
  addonId: null,
26495
26904
  access: "create"
26496
26905
  },
26906
+ "userPasskeys.getSecondFactorPreference": {
26907
+ capName: "user-passkeys",
26908
+ capScope: "system",
26909
+ addonId: null,
26910
+ access: "view"
26911
+ },
26497
26912
  "userPasskeys.listPasskeys": {
26498
26913
  capName: "user-passkeys",
26499
26914
  capScope: "system",
@@ -26506,6 +26921,12 @@ Object.freeze({
26506
26921
  addonId: null,
26507
26922
  access: "delete"
26508
26923
  },
26924
+ "userPasskeys.setSecondFactorPreference": {
26925
+ capName: "user-passkeys",
26926
+ capScope: "system",
26927
+ addonId: null,
26928
+ access: "create"
26929
+ },
26509
26930
  "vacuumControl.locate": {
26510
26931
  capName: "vacuum-control",
26511
26932
  capScope: "device",
@@ -26578,6 +26999,18 @@ Object.freeze({
26578
26999
  addonId: null,
26579
27000
  access: "view"
26580
27001
  },
27002
+ "viewerUi.getStaticDir": {
27003
+ capName: "viewer-ui",
27004
+ capScope: "system",
27005
+ addonId: null,
27006
+ access: "view"
27007
+ },
27008
+ "viewerUi.getVersion": {
27009
+ capName: "viewer-ui",
27010
+ capScope: "system",
27011
+ addonId: null,
27012
+ access: "view"
27013
+ },
26581
27014
  "waterHeater.setAway": {
26582
27015
  capName: "water-heater",
26583
27016
  capScope: "device",
@@ -26596,54 +27029,6 @@ Object.freeze({
26596
27029
  addonId: null,
26597
27030
  access: "create"
26598
27031
  },
26599
- "webrtc.closeSession": {
26600
- capName: "webrtc",
26601
- capScope: "system",
26602
- addonId: null,
26603
- access: "create"
26604
- },
26605
- "webrtc.createSession": {
26606
- capName: "webrtc",
26607
- capScope: "system",
26608
- addonId: null,
26609
- access: "create"
26610
- },
26611
- "webrtc.handleAnswer": {
26612
- capName: "webrtc",
26613
- capScope: "system",
26614
- addonId: null,
26615
- access: "create"
26616
- },
26617
- "webrtc.handleOffer": {
26618
- capName: "webrtc",
26619
- capScope: "system",
26620
- addonId: null,
26621
- access: "create"
26622
- },
26623
- "webrtc.hasAdaptiveBitrate": {
26624
- capName: "webrtc",
26625
- capScope: "system",
26626
- addonId: null,
26627
- access: "view"
26628
- },
26629
- "webrtc.registerStream": {
26630
- capName: "webrtc",
26631
- capScope: "system",
26632
- addonId: null,
26633
- access: "create"
26634
- },
26635
- "webrtc.supportsStream": {
26636
- capName: "webrtc",
26637
- capScope: "system",
26638
- addonId: null,
26639
- access: "view"
26640
- },
26641
- "webrtc.unregisterStream": {
26642
- capName: "webrtc",
26643
- capScope: "system",
26644
- addonId: null,
26645
- access: "delete"
26646
- },
26647
27032
  "webrtcSession.addIceCandidate": {
26648
27033
  capName: "webrtc-session",
26649
27034
  capScope: "device",