@camstack/addon-provider-reolink 1.1.21 → 1.1.23

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 +681 -288
  2. package/dist/addon.mjs +681 -288
  3. package/package.json +4 -1
package/dist/addon.mjs CHANGED
@@ -4650,7 +4650,7 @@ function _instanceof(cls, params = {}) {
4650
4650
  return inst;
4651
4651
  }
4652
4652
  //#endregion
4653
- //#region ../types/dist/sleep-CZDdRBua.mjs
4653
+ //#region ../types/dist/sleep-BC9Yqte7.mjs
4654
4654
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4655
4655
  EventCategory["SystemBoot"] = "system.boot";
4656
4656
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -4836,6 +4836,18 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
4836
4836
  */
4837
4837
  EventCategory["PipelineCameraUpdated"] = "pipeline.camera-updated";
4838
4838
  /**
4839
+ * The cluster camera-source OWNER changed (`clusterRoles.ingestNode`).
4840
+ * Emitted by addon-pipeline-orchestrator whenever it (re)derives node
4841
+ * capabilities — at boot, on agent online/offline, and on an ingest-node
4842
+ * flip. Carries the resolved `ownerNodeId`. The stream-broker consumes it to
4843
+ * keep its ingest-owner-gate decision current WITHOUT a per-`ensureBroker`
4844
+ * cross-process `getIngestOwner` query (push the authority's decision instead
4845
+ * of polling it on the hot path). Idempotent state — re-emitted on every
4846
+ * topology change, so a dropped event self-heals on the next one (plus the
4847
+ * broker's long backstop reconcile query).
4848
+ */
4849
+ EventCategory["PipelineIngestOwnerChanged"] = "pipeline.ingest-owner-changed";
4850
+ /**
4839
4851
  * Periodic snapshot of per-node pipeline-runner load
4840
4852
  * (`RunnerLocalLoad`). Emitted ~1Hz by every runner so UI dashboards
4841
4853
  * subscribe instead of polling `pipelineRunner.getLocalLoad`.
@@ -5359,10 +5371,6 @@ function hydrateField(field, values) {
5359
5371
  };
5360
5372
  }
5361
5373
  const rawValue = storedValue !== void 0 ? storedValue : defaultValue !== void 0 ? defaultValue : null;
5362
- if (field.type === "password") return {
5363
- ...field,
5364
- value: ""
5365
- };
5366
5374
  const value = field.type === "textarea" && field.isJson && rawValue !== null && typeof rawValue === "object" ? JSON.stringify(rawValue, null, 2) : rawValue;
5367
5375
  return {
5368
5376
  ...field,
@@ -6746,10 +6754,25 @@ function method(input, output, options) {
6746
6754
  timeoutMs: options?.timeoutMs
6747
6755
  };
6748
6756
  }
6757
+ /**
6758
+ * A wrapper/system-only method: served exclusively by the cap's system-level
6759
+ * provider (`InferProvider`), and OPTIONAL on `InferNativeProvider` so per-device
6760
+ * driver natives don't stub out a wrapper concern (e.g. a cross-device cache
6761
+ * overview). The `systemOnly: true` literal is what `InferNativeProvider` keys on.
6762
+ */
6763
+ function systemMethod(input, output, options) {
6764
+ return {
6765
+ ...method(input, output, options),
6766
+ systemOnly: true
6767
+ };
6768
+ }
6749
6769
  /** Shorthand to define an event schema */
6750
6770
  function event(data) {
6751
6771
  return { data };
6752
6772
  }
6773
+ var StaticDirOutputSchema$1 = object({ staticDir: string() });
6774
+ var VersionOutputSchema$1 = object({ version: string() });
6775
+ method(_void(), StaticDirOutputSchema$1), method(_void(), VersionOutputSchema$1);
6753
6776
  var StaticDirOutputSchema = object({ staticDir: string() });
6754
6777
  var VersionOutputSchema = object({ version: string() });
6755
6778
  method(_void(), StaticDirOutputSchema), method(_void(), VersionOutputSchema);
@@ -6943,6 +6966,36 @@ var ModelFormatsSchema = object({
6943
6966
  tflite: ModelFormatEntrySchema.optional(),
6944
6967
  pt: ModelFormatEntrySchema.optional()
6945
6968
  });
6969
+ /**
6970
+ * Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
6971
+ * the reduced `PipelineModelOption` returned in `pipeline.getSchema()` so the
6972
+ * grouped Family→Tier→Variant picker renders identically in the config UI and
6973
+ * in the pipeline/device steppers. The flat `id` stays the source of truth for
6974
+ * resolution/download/persistence; this is a presentation overlay resolved back
6975
+ * to an `id`.
6976
+ */
6977
+ var ModelVariantGroupSchema = object({
6978
+ /** Top-level family, e.g. `yolo26` (later `d-fine`, `rf-detr`). */
6979
+ family: string(),
6980
+ /** Size within the family, e.g. `n` | `s` | `m` | `l`. */
6981
+ tier: string(),
6982
+ /** Quantization axis. Omit ⇒ the fp32 base build. */
6983
+ precision: _enum(["fp32", "int8"]).optional(),
6984
+ /**
6985
+ * Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
6986
+ * latency-optimized export (e.g. ReLU-activation variant) — the slot the
6987
+ * future performance variants plug into.
6988
+ */
6989
+ optimization: _enum(["standard", "fast"]).optional(),
6990
+ /**
6991
+ * Input-resolution axis (square input side, px). Omit ⇒ the family's native
6992
+ * resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
6993
+ * cheap latency lever — especially on Apple ANE and the Intel N100 — at a
6994
+ * small-object accuracy cost. Mirrors the model's `inputSize` but lifted onto
6995
+ * the group so the selector can offer it as a variant axis.
6996
+ */
6997
+ resolution: number().int().positive().optional()
6998
+ });
6946
6999
  var ModelCatalogEntrySchema = object({
6947
7000
  id: string(),
6948
7001
  name: string(),
@@ -6972,7 +7025,43 @@ var ModelCatalogEntrySchema = object({
6972
7025
  * Auxiliary files required at runtime (labels JSON, charset dict, etc.).
6973
7026
  * Downloaded into the same modelsDir alongside the model file.
6974
7027
  */
6975
- extraFiles: array(ModelExtraFileSchema).readonly().optional()
7028
+ extraFiles: array(ModelExtraFileSchema).readonly().optional(),
7029
+ /**
7030
+ * LEGACY entry — retained in the catalog so a persisted operator selection
7031
+ * still RESOLVES (and can be re-activated), but hidden from the selectable
7032
+ * model list and excluded from the auto format-default pick. Set on the
7033
+ * superseded / consolidated models (older lineages, redundant fp16 IRs) so
7034
+ * the active lineup stays the coherent curated ladder without deleting a
7035
+ * model anyone may still be pinned to. `resolveModelForFormat` keeps honoring
7036
+ * an explicit legacy id that has a build for the node's format.
7037
+ */
7038
+ legacy: boolean().optional(),
7039
+ /**
7040
+ * Measured quality/latency metadata — populated from the benchmark addon on
7041
+ * the real node classes. Absent = not yet measured (most entries today; the
7042
+ * catalog historically carried only `sizeMB`, a poor cross-architecture
7043
+ * speed proxy). `p95LatencyMs` is keyed by node class (e.g. `n100`, `mac`).
7044
+ */
7045
+ metrics: object({
7046
+ map50: number().optional(),
7047
+ p95LatencyMs: record(string(), number()).optional()
7048
+ }).optional(),
7049
+ /**
7050
+ * SPDX-ish license id of the model weights (e.g. `AGPL-3.0` for Ultralytics
7051
+ * YOLO26, `GPL-3.0` for YOLOv9, `Apache-2.0` for D-FINE/RF-DETR). Matters for
7052
+ * the retraining addon and any future commercial distribution.
7053
+ */
7054
+ license: string().optional(),
7055
+ /**
7056
+ * Variant-selector grouping. The UI groups models by `family` + `tier` and
7057
+ * offers `precision` / `optimization` as variant axes WITHIN a tier — so all
7058
+ * of a family's sizes and quantizations collapse into one grouped picker
7059
+ * instead of a flat list of `yolo26s`, `yolo26s-int8`, … Absent ⇒ ungrouped
7060
+ * (legacy / custom models) — never shown in the grouped selector. The flat
7061
+ * `id` stays the source of truth for resolution/download/persistence; grouping
7062
+ * is a presentation overlay resolved back to an `id`.
7063
+ */
7064
+ group: ModelVariantGroupSchema.optional()
6976
7065
  });
6977
7066
  var ConvertTargetSchema = discriminatedUnion("format", [object({
6978
7067
  format: literal("openvino"),
@@ -7208,8 +7297,8 @@ var RecordingModeSchema = _enum([
7208
7297
  "onAudioThreshold"
7209
7298
  ]);
7210
7299
  /**
7211
- * First-class, authoritative per-camera storage mode — the netta choice the UI
7212
- * reads directly (never inferred from `rules`):
7300
+ * First-class, authoritative per-camera storage mode — the explicit choice the
7301
+ * UI reads directly (never inferred from `rules`):
7213
7302
  * - `off` — not recording.
7214
7303
  * - `events` — record only around triggers (motion / audio threshold),
7215
7304
  * with pre/post-buffer.
@@ -9411,26 +9500,13 @@ onBrightnessChanged: { data: object({
9411
9500
  */
9412
9501
  runtimeState: BrightnessStatusSchema
9413
9502
  };
9503
+ /** Stream delivery format. (Relocated from the retired `streaming-engine` cap.) */
9414
9504
  var StreamFormatSchema = _enum([
9415
9505
  "webrtc",
9416
9506
  "hls",
9417
9507
  "mjpeg",
9418
9508
  "rtsp"
9419
9509
  ]);
9420
- var StreamInfoSchema = object({
9421
- streamId: string(),
9422
- format: StreamFormatSchema,
9423
- url: string().nullable(),
9424
- active: boolean()
9425
- });
9426
- method(object({
9427
- streamId: string(),
9428
- sourceUrl: string(),
9429
- codec: string().optional()
9430
- }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), _void(), { kind: "mutation" }), method(object({
9431
- streamId: string(),
9432
- format: StreamFormatSchema
9433
- }), string().nullable()), method(_void(), array(StreamInfoSchema));
9434
9510
  var RtspRestreamEntrySchema = object({
9435
9511
  brokerId: string(),
9436
9512
  url: string(),
@@ -10298,37 +10374,7 @@ var consumablesCapability = {
10298
10374
  scope: "device",
10299
10375
  deviceNative: true,
10300
10376
  mode: "singleton",
10301
- deviceTypes: [
10302
- DeviceType.Camera,
10303
- DeviceType.Hub,
10304
- DeviceType.Light,
10305
- DeviceType.Siren,
10306
- DeviceType.Switch,
10307
- DeviceType.Sensor,
10308
- DeviceType.Thermostat,
10309
- DeviceType.Button,
10310
- DeviceType.EventEmitter,
10311
- DeviceType.Update,
10312
- DeviceType.Generic,
10313
- DeviceType.Notifier,
10314
- DeviceType.Script,
10315
- DeviceType.Automation,
10316
- DeviceType.Lock,
10317
- DeviceType.Cover,
10318
- DeviceType.Valve,
10319
- DeviceType.Humidifier,
10320
- DeviceType.WaterHeater,
10321
- DeviceType.Fan,
10322
- DeviceType.MediaPlayer,
10323
- DeviceType.AlarmPanel,
10324
- DeviceType.Control,
10325
- DeviceType.Presence,
10326
- DeviceType.Weather,
10327
- DeviceType.Vacuum,
10328
- DeviceType.LawnMower,
10329
- DeviceType.Container,
10330
- DeviceType.Image
10331
- ],
10377
+ deviceTypes: Object.values(DeviceType),
10332
10378
  deviceConfig: { ui: {
10333
10379
  kind: "widget",
10334
10380
  widgetId: "host/consumables-panel",
@@ -11786,7 +11832,7 @@ var BoundingBoxSchema = object({
11786
11832
  w: number(),
11787
11833
  h: number()
11788
11834
  });
11789
- var SpatialDetectionSchema = object({
11835
+ object({
11790
11836
  class: string(),
11791
11837
  originalClass: string(),
11792
11838
  score: number(),
@@ -11921,7 +11967,6 @@ var PipelineDefaultStepSchema = lazy(() => object({
11921
11967
  enabled: boolean(),
11922
11968
  modelId: string(),
11923
11969
  children: array(PipelineDefaultStepSchema).readonly(),
11924
- engine: PipelineEngineChoiceSchema.optional(),
11925
11970
  group: string().optional(),
11926
11971
  settings: record(string(), unknown()).optional()
11927
11972
  }));
@@ -11946,7 +11991,9 @@ var PipelineModelOptionSchema = object({
11946
11991
  formats: record(string(), object({
11947
11992
  downloaded: boolean(),
11948
11993
  sizeMB: number()
11949
- }))
11994
+ })),
11995
+ group: ModelVariantGroupSchema.optional(),
11996
+ legacy: boolean().optional()
11950
11997
  });
11951
11998
  var ConfigFieldBridge = custom$2();
11952
11999
  var PipelineAddonSchemaSchema = object({
@@ -11960,6 +12007,7 @@ var PipelineAddonSchemaSchema = object({
11960
12007
  defaultModelId: string(),
11961
12008
  defaultModelIdByFormat: record(string(), string()).optional(),
11962
12009
  enabledByDefault: boolean().optional(),
12010
+ backfillIntoExistingOverrides: boolean().optional(),
11963
12011
  defaultConfidence: number(),
11964
12012
  group: string().optional(),
11965
12013
  configSchema: array(ConfigFieldBridge).readonly().optional()
@@ -11976,11 +12024,6 @@ var PipelineSchemaSchema = object({
11976
12024
  selectedEngine: PipelineEngineChoiceSchema,
11977
12025
  slots: array(PipelineSlotSchemaSchema).readonly()
11978
12026
  });
11979
- var DetectorOutputSchema = object({
11980
- detections: array(SpatialDetectionSchema).readonly(),
11981
- inferenceMs: number(),
11982
- modelId: string()
11983
- });
11984
12027
  var EngineProvisioningSchema = object({
11985
12028
  runtimeId: _enum([
11986
12029
  "onnx",
@@ -11997,15 +12040,42 @@ var EngineProvisioningSchema = object({
11997
12040
  ]),
11998
12041
  progress: number().optional(),
11999
12042
  error: string().optional(),
12000
- nextRetryAt: number().optional()
12043
+ nextRetryAt: number().optional(),
12044
+ /**
12045
+ * Gate A (config-correctness gate at engine change): human-readable
12046
+ * config issues surfaced EAGERLY when the node's engine changes — model
12047
+ * substitutions ("chose X, running Y") and zero-build steps ("no model
12048
+ * has a <format> build"). Additive/optional: informational only, never
12049
+ * enforced here — `assertEngineReady` (readiness) still gates inference.
12050
+ * Absent/empty when the node-default tree resolves cleanly.
12051
+ */
12052
+ configIssues: array(string()).optional()
12001
12053
  });
12002
12054
  var PipelineStepInputSchema = lazy(() => object({
12003
12055
  addonId: string(),
12004
- modelId: string(),
12056
+ modelId: string().optional(),
12005
12057
  enabled: boolean().default(true),
12006
12058
  children: array(PipelineStepInputSchema).optional(),
12007
12059
  settings: record(string(), unknown()).optional()
12008
12060
  }));
12061
+ var ModelSubstitutionSchema = object({
12062
+ addonId: string(),
12063
+ chosen: string(),
12064
+ running: string(),
12065
+ format: string()
12066
+ });
12067
+ var PipelineValidationIssueSchema = object({
12068
+ addonId: string(),
12069
+ kind: _enum(["unknown-addon", "no-format-build"]),
12070
+ detail: string()
12071
+ });
12072
+ var PipelineValidationResultSchema = object({
12073
+ ok: boolean(),
12074
+ issues: array(PipelineValidationIssueSchema).readonly(),
12075
+ substitutions: array(ModelSubstitutionSchema).readonly(),
12076
+ /** The node's `currentEngine.format` this validation ran against. */
12077
+ format: string()
12078
+ });
12009
12079
  var ReferenceImageEntrySchema = object({
12010
12080
  filename: string(),
12011
12081
  stepIds: array(string()).readonly().optional()
@@ -12076,7 +12146,13 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12076
12146
  })) }), object({ success: literal(true) }), {
12077
12147
  kind: "mutation",
12078
12148
  auth: "admin"
12079
- }), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
12149
+ }), method(object({ nodeId: string() }), object({
12150
+ success: literal(true),
12151
+ clearedDevices: number()
12152
+ }), {
12153
+ kind: "mutation",
12154
+ auth: "admin"
12155
+ }), 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({
12080
12156
  name: string(),
12081
12157
  steps: array(PipelineTemplateStepSchema).readonly(),
12082
12158
  engine: PipelineEngineChoiceSchema
@@ -12093,10 +12169,6 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12093
12169
  modelId: string(),
12094
12170
  format: ModelFormatSchema$1
12095
12171
  }), object({ success: literal(true) }), { kind: "mutation" }), method(object({
12096
- addonId: string(),
12097
- frame: FrameInputSchema,
12098
- config: record(string(), unknown()).optional()
12099
- }), DetectorOutputSchema), method(object({
12100
12172
  engine: PipelineEngineChoiceSchema.optional(),
12101
12173
  steps: array(PipelineStepInputSchema).min(1),
12102
12174
  frame: FrameInputSchema.optional(),
@@ -12275,6 +12347,25 @@ var zonesCapability = {
12275
12347
  runtimeState: object({ zones: array(ZoneSchema).readonly() })
12276
12348
  };
12277
12349
  /**
12350
+ * A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
12351
+ * decode worker resolves it against the RETAINED native frame's real pixel dims,
12352
+ * so the caller supplies only the detection-res bbox divided by the detection
12353
+ * dims — no native resolution to plumb.
12354
+ */
12355
+ var NativeCropBboxSchema = object({
12356
+ x: number(),
12357
+ y: number(),
12358
+ w: number(),
12359
+ h: number()
12360
+ });
12361
+ /** Result of a best-effort native-resolution crop (`getNativeCrop`). */
12362
+ var NativeCropResultSchema = object({
12363
+ /** Packed rgb (24-bit) pixels of the crop. */
12364
+ bytes: _instanceof(Uint8Array),
12365
+ width: number().int().positive(),
12366
+ height: number().int().positive()
12367
+ });
12368
+ /**
12278
12369
  * Per-camera tunable ranges + defaults. Single source of truth used
12279
12370
  * by both the Zod data schema (validation + default fallback) and
12280
12371
  * the device settings UI (slider min/max/step). Touch one place and
@@ -12369,6 +12460,13 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
12369
12460
  kind: literal("remote-restream"),
12370
12461
  /** The camera's source-owner node (slice 1: always the hub). */
12371
12462
  ownerNodeId: string(),
12463
+ /**
12464
+ * The owner's LAN-reachable host, resolved by the orchestrator from the
12465
+ * per-node `reachableHost` override (Cluster UI). When present the runner
12466
+ * dials THIS host for the owner's restream, in preference to the
12467
+ * `CAMSTACK_HUB_URL`-derived default. Absent → auto-detect fallback.
12468
+ */
12469
+ ownerReachableHost: string().optional(),
12372
12470
  /** Operator override for the owner host the runner dials. */
12373
12471
  hubHostnameOverride: string().optional()
12374
12472
  })]).describe("Per-camera frame-source mode for the runner (P2c)");
@@ -12377,13 +12475,11 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
12377
12475
  * specific runner instance via `attachCamera`. Carries everything the
12378
12476
  * runner needs to subscribe to the local broker and execute inference.
12379
12477
  *
12380
- * Stateless-pipeline model: the full pipeline content (`engine`, `steps`,
12381
- * optional `audio`) travels with the attach payload. The runner keeps it
12382
- * in RAM for the lifetime of the attach — on rebalance, edit, or
12383
- * restart the orchestrator re-sends the latest snapshot.
12384
- *
12385
- * `engine`/`steps`/`audio` are optional during the additive migration
12386
- * window; once orchestrator + UI are migrated they become required.
12478
+ * Stateless-pipeline model: the pipeline content (`steps`, optional
12479
+ * `audio`) travels with the attach payload. The runner keeps it in RAM
12480
+ * for the lifetime of the attach — on rebalance, edit, or restart the
12481
+ * orchestrator re-sends the latest snapshot. Engine is NOT carried: it is
12482
+ * node-local, resolved by the executing runner at dispatch time.
12387
12483
  */
12388
12484
  var RunnerCameraConfigSchema = object({
12389
12485
  deviceId: number(),
@@ -12434,14 +12530,11 @@ var RunnerCameraConfigSchema = object({
12434
12530
  */
12435
12531
  motionSources: MotionSourcesSchema.default(["analyzer"]),
12436
12532
  pipelineEnabled: boolean().default(true),
12437
- /** Engine choice for video steps (runtime+backend+format). */
12438
- engine: PipelineEngineChoiceSchema.optional(),
12439
12533
  /** Ordered tree of video steps. Absent → runner skips video detection. */
12440
12534
  steps: array(PipelineStepInputSchema).readonly().optional(),
12441
12535
  /** Audio classification branch. `enabled:false` disables, null skips. */
12442
12536
  audio: object({
12443
- engine: PipelineEngineChoiceSchema,
12444
- modelId: string(),
12537
+ modelId: string().optional(),
12445
12538
  enabled: boolean()
12446
12539
  }).nullable().optional(),
12447
12540
  /**
@@ -12528,7 +12621,11 @@ var RunnerLocalMetricsSchema = object({
12528
12621
  avgInferenceTimeMs: number(),
12529
12622
  queueDepth: number()
12530
12623
  });
12531
- 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());
12624
+ 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({
12625
+ handle: FrameHandleSchema,
12626
+ bbox: NativeCropBboxSchema,
12627
+ maxWidth: number().int().positive().optional()
12628
+ }), NativeCropResultSchema.nullable());
12532
12629
  /**
12533
12630
  * Hardware / firmware motion sensor cap — binary detected state plus
12534
12631
  * a timestamp of the last observation. Distinct from
@@ -15459,7 +15556,9 @@ var AddonPageDeclarationSchema$1 = object({
15459
15556
  icon: string(),
15460
15557
  path: string(),
15461
15558
  remoteName: string(),
15462
- bundle: string()
15559
+ bundle: string(),
15560
+ section: string().optional(),
15561
+ sectionLabel: string().optional()
15463
15562
  });
15464
15563
  var AddonPageInfoSchema = object({
15465
15564
  addonId: string(),
@@ -15499,7 +15598,18 @@ var AddonPageDeclarationSchema = object({
15499
15598
  * the static-file route can compute an mtime-based cache-buster URL
15500
15599
  * without a separate filesystem stat.
15501
15600
  */
15502
- bundle: string()
15601
+ bundle: string(),
15602
+ /**
15603
+ * Sidebar section this page docks into. Well-known ids: `'detection'`,
15604
+ * `'cluster'`, `'administration'` — the page renders inside that group.
15605
+ * Any OTHER string creates (or joins) a custom section rendered after
15606
+ * the built-in groups; its label comes from `sectionLabel` (first
15607
+ * declaration wins), falling back to the id. Absent → the legacy
15608
+ * "Addon Pages" group.
15609
+ */
15610
+ section: string().optional(),
15611
+ /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
15612
+ sectionLabel: string().optional()
15503
15613
  });
15504
15614
  method(_void(), array(AddonPageDeclarationSchema).readonly());
15505
15615
  var AddonHttpRouteSchema = object({
@@ -15715,6 +15825,17 @@ var WidgetMetadataSchema = object({
15715
15825
  deviceContext: boolean().default(false),
15716
15826
  integrationContext: boolean().default(false)
15717
15827
  }),
15828
+ /**
15829
+ * Loadable BEFORE authentication. The normal widget registry listing
15830
+ * (`addon-widgets.listWidgets`) is auth-gated, so a pre-auth surface
15831
+ * (the login page) cannot discover a widget through it. A widget that
15832
+ * declares `preAuth: true` marks itself as safe to mount on a pre-auth
15833
+ * screen — it is surfaced through the PUBLIC `auth.listLoginMethods`
15834
+ * login-method contribution channel (see `login-method.cap.ts`) rather
15835
+ * than the authenticated registry, and its bundle is served by the
15836
+ * public `/api/addon-widgets/:addonId/*` static route. Defaults false.
15837
+ */
15838
+ preAuth: boolean().optional().default(false),
15718
15839
  /** Dashboard placement HINTS (operator can override per instance). */
15719
15840
  defaultSize: WidgetSizeEnum.default("md"),
15720
15841
  allowedSizes: array(WidgetSizeEnum).readonly().default([
@@ -16016,6 +16137,66 @@ method(object({
16016
16137
  password: string()
16017
16138
  }), AuthResultSchema.nullable(), { kind: "mutation" }), method(object({ state: string() }), string()), method(record(string(), string()), AuthResultSchema, { kind: "mutation" }), method(object({ token: string() }), AuthResultSchema.nullable());
16018
16139
  /**
16140
+ * `login-method` — collection cap through which auth addons contribute
16141
+ * their pre-auth login surfaces to the login page. This is the SINGLE,
16142
+ * generic mechanism that supersedes the dead `auth.listProviders` reader:
16143
+ * every auth addon (OIDC, magic-link, WebAuthn/passkey) registers a
16144
+ * `login-method` provider and the PUBLIC `auth.listLoginMethods`
16145
+ * procedure aggregates them for the unauthenticated login page.
16146
+ *
16147
+ * A contribution is a discriminated union on `kind`:
16148
+ *
16149
+ * - `redirect` — a declarative button. The login page renders a generic
16150
+ * button that navigates to `startUrl` (an addon-owned HTTP route).
16151
+ * Covers OIDC (`/addon/auth-oidc/<id>/start`) and magic-link with
16152
+ * ZERO shell-side JS. A future SSO addon plugs in the same way — the
16153
+ * login page needs NO change.
16154
+ *
16155
+ * - `widget` — a Module-Federation widget the login page mounts (via
16156
+ * `loadRemoteBundle`) for an in-page ceremony. Covers the passkey
16157
+ * login ceremony, which must run `@simplewebauthn/browser` INSIDE the
16158
+ * addon bundle. The referenced widget also declares `preAuth: true` in
16159
+ * its `addon-widgets-source` catalog entry. `auth.listLoginMethods`
16160
+ * stamps a public `bundleUrl` from `addonId` + `bundle`.
16161
+ *
16162
+ * Every contribution carries a `stage`:
16163
+ * - `primary` — shown on the first credentials screen (OIDC /
16164
+ * magic-link buttons; a future usernameless passkey).
16165
+ * - `second-factor` — shown AFTER the password leg, gated on the
16166
+ * returned `factors` (passkey-as-2FA today).
16167
+ *
16168
+ * `mount: skip` — the cap is read server-side by the core auth router
16169
+ * (`registry.getCollection('login-method')`), never mounted as its own
16170
+ * tRPC router.
16171
+ */
16172
+ /** When a login method renders in the two-phase login flow. */
16173
+ var LoginStageEnum = _enum(["primary", "second-factor"]);
16174
+ /** One login-method contribution — redirect button OR pre-auth widget. */
16175
+ var LoginMethodContributionSchema = discriminatedUnion("kind", [object({
16176
+ kind: literal("redirect"),
16177
+ /** Stable id within the login-method set (e.g. `auth-oidc/google`). */
16178
+ id: string(),
16179
+ /** Operator-facing button label. */
16180
+ label: string(),
16181
+ /** lucide-react icon name. */
16182
+ icon: string().optional(),
16183
+ /** Addon-owned HTTP route the button navigates to (GET). */
16184
+ startUrl: string(),
16185
+ stage: LoginStageEnum
16186
+ }), object({
16187
+ kind: literal("widget"),
16188
+ /** Stable id within the login-method set (e.g. `auth-webauthn/passkey-login`). */
16189
+ id: string(),
16190
+ /** Owning addon id — drives the public bundle URL + the MF namespace. */
16191
+ addonId: string(),
16192
+ /** Bundle filename inside the addon's dist dir (`remoteEntry.js`). */
16193
+ bundle: string(),
16194
+ /** MF remote descriptor — `{ remoteName, exposedModule, componentKey }`. */
16195
+ remote: WidgetRemoteSchema,
16196
+ stage: LoginStageEnum
16197
+ })]);
16198
+ method(_void(), array(LoginMethodContributionSchema).readonly());
16199
+ /**
16019
16200
  * Orchestrator-side destination metadata. The orchestrator computes
16020
16201
  * `id = <addonId>:<subId>` from its provider lookup so consumers
16021
16202
  * (admin UI, restore flow) see one canonical key.
@@ -18131,7 +18312,17 @@ var TrackSchema = object({
18131
18312
  /** Cumulative normalized distance travelled (0..1 units = full frame width). */
18132
18313
  totalDistance: number(),
18133
18314
  state: TrackStateSchema,
18134
- active: boolean()
18315
+ active: boolean(),
18316
+ /** Deterministic key-event importance score in [0,1] (server-computed at
18317
+ * track expiry, recomputed on late label). Absent on legacy rows written
18318
+ * before scoring shipped — consumers degrade to absence / compute-on-read. */
18319
+ importance: number().optional(),
18320
+ /** Id of the track's highest-confidence ObjectEvent (its representative
18321
+ * "best" frame). Absent when the track produced no object events. */
18322
+ bestEventId: string().optional(),
18323
+ /** Tag of the importance sub-signal that dominated the score
18324
+ * (identity|dwell|proximity|class|confidence|travel|zone). */
18325
+ importanceReason: string().optional()
18135
18326
  });
18136
18327
  var BaseEventFields = {
18137
18328
  id: string(),
@@ -18196,8 +18387,18 @@ var ObjectEventSchema = object({
18196
18387
  frameHeight: number().optional(),
18197
18388
  /** MediaStore key for the crop attached to this event (if any). */
18198
18389
  mediaKey: string().optional(),
18390
+ /** Design B: MediaStore key of the track's native-resolution key frame (the
18391
+ * best-detection full frame). Resolve via the event-media data-plane
18392
+ * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
18393
+ * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
18394
+ * sources — consumers fall back to `mediaKey` (the tight crop). */
18395
+ keyFrameMediaKey: string().optional(),
18199
18396
  /** Populated by B5 (recording playback URL for this event). */
18200
- mediaUrl: string().optional()
18397
+ mediaUrl: string().optional(),
18398
+ /** The parent track's key-event importance [0,1], propagated to every object
18399
+ * event of the track (so an event row can be sorted by importance without a
18400
+ * track join). Absent on legacy rows / before the track was scored. */
18401
+ importance: number().optional()
18201
18402
  });
18202
18403
  var AudioEventSchema = object({
18203
18404
  ...BaseEventFields,
@@ -18221,7 +18422,8 @@ var MediaFileKindEnum = _enum([
18221
18422
  "fullFrame",
18222
18423
  "fullFrameBoxed",
18223
18424
  "faceCrop",
18224
- "plateCrop"
18425
+ "plateCrop",
18426
+ "keyFrame"
18225
18427
  ]);
18226
18428
  var MediaFileSchema = object({
18227
18429
  key: string(),
@@ -18242,6 +18444,32 @@ var DeviceEventQueryInput = object({
18242
18444
  projection: _enum(["full", "slim"]).optional()
18243
18445
  });
18244
18446
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
18447
+ var KeyEventQueryInput = object({
18448
+ deviceId: number(),
18449
+ /** Window lower bound (track firstSeen ≥ since). */
18450
+ since: number(),
18451
+ /** Window upper bound (track firstSeen ≤ until). */
18452
+ until: number(),
18453
+ limit: number().int().min(1).max(200).default(50),
18454
+ /** Drop tracks scoring below this importance. */
18455
+ minImportance: number().min(0).max(1).optional(),
18456
+ /** Restrict to a single class (e.g. 'person'). */
18457
+ classFilter: string().optional()
18458
+ });
18459
+ var KeyEventSchema = object({
18460
+ /** The representative event id (the track's best ObjectEvent, else its trackId). */
18461
+ id: string(),
18462
+ trackId: string(),
18463
+ /** Track start time (firstSeen). */
18464
+ timestamp: number(),
18465
+ className: string(),
18466
+ label: string().optional(),
18467
+ importance: number(),
18468
+ /** Highest-confidence ObjectEvent id for the track (empty when none). */
18469
+ bestEventId: string(),
18470
+ /** Track lifetime in ms (lastSeen - firstSeen). */
18471
+ windowMs: number().optional()
18472
+ });
18245
18473
  var TrackedDetectionSchema = object({
18246
18474
  trackId: string(),
18247
18475
  className: string(),
@@ -18271,7 +18499,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18271
18499
  }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
18272
18500
  kind: "mutation",
18273
18501
  auth: "admin"
18274
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({
18502
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
18275
18503
  deviceId: number(),
18276
18504
  since: number(),
18277
18505
  until: number(),
@@ -18316,11 +18544,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18316
18544
  timestamp: number()
18317
18545
  });
18318
18546
  var CameraPipelineConfigSchema = object({
18319
- engine: PipelineEngineChoiceSchema,
18547
+ engine: PipelineEngineChoiceSchema.optional(),
18320
18548
  steps: array(PipelineStepInputSchema).readonly(),
18321
18549
  audio: object({
18322
- engine: PipelineEngineChoiceSchema,
18323
- modelId: string(),
18550
+ engine: PipelineEngineChoiceSchema.optional(),
18551
+ modelId: string().optional(),
18324
18552
  enabled: boolean(),
18325
18553
  settings: record(string(), unknown()).readonly().optional()
18326
18554
  }).nullable().optional()
@@ -18335,7 +18563,7 @@ var PipelineTemplateSchema = object({
18335
18563
  });
18336
18564
  var AgentAddonConfigSchema = object({
18337
18565
  enabled: boolean(),
18338
- modelId: string(),
18566
+ modelId: string().optional(),
18339
18567
  settings: record(string(), unknown()).readonly()
18340
18568
  });
18341
18569
  var AgentPipelineSettingsSchema = object({
@@ -18345,12 +18573,25 @@ var AgentPipelineSettingsSchema = object({
18345
18573
  detectWeight: number().positive().optional(),
18346
18574
  /** Node is eligible to run the detection pipeline (decode + inference). */
18347
18575
  detect: boolean().optional(),
18348
- /** Node is eligible to host decoder sessions. */
18576
+ /**
18577
+ * DEPRECATED AND IGNORED. Decode is always co-located with its frame
18578
+ * consumer, so decode eligibility IS detect eligibility. Kept optional in
18579
+ * the schema ONLY so persisted stores written before the removal still
18580
+ * parse — no code reads it and no write path emits it.
18581
+ */
18349
18582
  decode: boolean().optional(),
18350
18583
  /** Node is eligible to run audio-analyzer sessions. */
18351
18584
  audio: boolean().optional(),
18352
18585
  /** Node is eligible to be the ingest / source-owner (serve the restream). */
18353
- ingest: boolean().optional()
18586
+ ingest: boolean().optional(),
18587
+ /**
18588
+ * Operator override for the LAN host a cross-node decoder dials to reach
18589
+ * THIS node's restream (Cluster UI). Absent → auto-detect: a remote runner
18590
+ * falls back to its `CAMSTACK_HUB_URL`-derived host (the Moleculer address
18591
+ * it already uses to reach the hub). Set this only when the auto-detected
18592
+ * address is wrong (multi-homed host, NAT, custom interface).
18593
+ */
18594
+ reachableHost: string().optional()
18354
18595
  });
18355
18596
  var CameraPipelineForAgentSchema = object({
18356
18597
  steps: array(PipelineStepInputSchema).readonly(),
@@ -18398,25 +18639,6 @@ var PipelineAssignmentSchema = object({
18398
18639
  assignedAt: number()
18399
18640
  });
18400
18641
  /**
18401
- * Decoder placement record. Symmetric to `PipelineAssignmentSchema` but for
18402
- * the decoder-node placement domain (`balanceDecoder` decision: manual pin
18403
- * → co-located with pipeline → capacity).
18404
- */
18405
- var DecoderAssignmentSchema = object({
18406
- deviceId: number(),
18407
- /** Moleculer node id of the decoder provider currently responsible for this camera. */
18408
- decoderNodeId: string(),
18409
- /** True when the assignment was set manually via `assignDecoder`, false when chosen by the balancer. */
18410
- pinned: boolean(),
18411
- /** Why this assignment was made — useful for debugging the decoder balancer. */
18412
- reason: _enum([
18413
- "manual",
18414
- "co-located",
18415
- "capacity",
18416
- "hardware-affinity"
18417
- ])
18418
- });
18419
- /**
18420
18642
  * Per-agent load summary surfaced to the load balancer + dashboards.
18421
18643
  * Aggregated from each runner's `getLocalLoad` cap call.
18422
18644
  */
@@ -18456,6 +18678,15 @@ var GlobalMetricsSchema = object({
18456
18678
  * capability providers.
18457
18679
  */
18458
18680
  var CapabilityBindingsSchema = record(string(), string());
18681
+ /**
18682
+ * The cluster's single camera-source owner (`clusterRoles.ingestNode`) plus
18683
+ * its LAN-reachable host, if one is registered. See `getIngestOwner`.
18684
+ */
18685
+ var IngestOwnerSchema = object({
18686
+ ownerNodeId: string(),
18687
+ reachableHost: string().optional(),
18688
+ configIssue: string().optional()
18689
+ });
18459
18690
  /** Source block — always present; derives from the stream catalog. */
18460
18691
  var CameraSourceStatusSchema = object({ streams: array(object({
18461
18692
  camStreamId: string(),
@@ -18470,6 +18701,14 @@ var CameraAssignmentStatusSchema = object({
18470
18701
  detectionNodeId: string().nullable(),
18471
18702
  decoderNodeId: string().nullable(),
18472
18703
  audioNodeId: string().nullable(),
18704
+ /**
18705
+ * The node that OWNS this camera's physical source pull (dials the RTSP and
18706
+ * hosts the broker/restream) — the cluster ingest owner today
18707
+ * (`clusterRoles.ingestNode`), per-camera once source assignment lands. Lets
18708
+ * the UI show WHERE a camera is sourced without SSH/logs, and is the node the
18709
+ * broker block below was read from (pinned). Nullable only pre-wiring.
18710
+ */
18711
+ sourceNodeId: string().nullable(),
18473
18712
  pinned: object({
18474
18713
  detection: boolean(),
18475
18714
  decoder: boolean(),
@@ -18602,16 +18841,7 @@ method(object({
18602
18841
  }), object({ success: literal(true) }), {
18603
18842
  kind: "mutation",
18604
18843
  auth: "admin"
18605
- }), method(object({
18606
- deviceId: number(),
18607
- nodeId: string()
18608
- }), _void(), {
18609
- kind: "mutation",
18610
- auth: "admin"
18611
- }), method(object({ deviceId: number() }), _void(), {
18612
- kind: "mutation",
18613
- auth: "admin"
18614
- }), method(_void(), array(DecoderAssignmentSchema).readonly()), method(object({
18844
+ }), method(_void(), IngestOwnerSchema), method(object({
18615
18845
  deviceId: number(),
18616
18846
  nodeId: string()
18617
18847
  }), object({ success: literal(true) }), {
@@ -18632,10 +18862,7 @@ method(object({
18632
18862
  nodeId: string(),
18633
18863
  pinned: boolean(),
18634
18864
  assignedAt: number()
18635
- }))), method(object({
18636
- deviceId: number(),
18637
- pipelineNodeId: string().optional()
18638
- }), DecoderAssignmentSchema), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
18865
+ }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
18639
18866
  nodeId: string(),
18640
18867
  settings: AgentPipelineSettingsSchema
18641
18868
  })).readonly()), method(object({
@@ -18665,12 +18892,26 @@ method(object({
18665
18892
  }), method(object({
18666
18893
  agentNodeId: string(),
18667
18894
  detect: boolean().nullable().optional(),
18668
- decode: boolean().nullable().optional(),
18669
18895
  audio: boolean().nullable().optional(),
18670
18896
  ingest: boolean().nullable().optional()
18671
18897
  }), object({ success: literal(true) }), {
18672
18898
  kind: "mutation",
18673
18899
  auth: "admin"
18900
+ }), method(object({
18901
+ agentNodeId: string(),
18902
+ reachableHost: string().nullable()
18903
+ }), object({ success: literal(true) }), {
18904
+ kind: "mutation",
18905
+ auth: "admin"
18906
+ }), method(object({ agentNodeId: string() }), object({
18907
+ success: literal(true),
18908
+ /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
18909
+ effectiveModelId: string().nullable(),
18910
+ /** Number of cameras whose node-scoped overrides were cleared. */
18911
+ clearedCameraOverrides: number()
18912
+ }), {
18913
+ kind: "mutation",
18914
+ auth: "admin"
18674
18915
  }), method(object({ deviceId: number() }), CameraPipelineSettingsSchema.nullable()), method(object({
18675
18916
  deviceId: number(),
18676
18917
  addonId: string(),
@@ -18715,22 +18956,131 @@ method(object({
18715
18956
  kind: "mutation",
18716
18957
  auth: "admin"
18717
18958
  });
18718
- var RegisteredStreamSchema = object({
18719
- streamId: string(),
18720
- label: string().optional(),
18721
- codec: string(),
18722
- type: _enum(["video", "audio"]),
18723
- sourceUrl: string()
18959
+ /**
18960
+ * server-management — per-NODE singleton capability for a node's ROOT
18961
+ * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
18962
+ * agents).
18963
+ *
18964
+ * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
18965
+ * on agents) carries the whole software stack in its npm dep tree, so ONE
18966
+ * version describes the node. Updates install into
18967
+ * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
18968
+ * starter (probation boot + auto-rollback to N-1).
18969
+ *
18970
+ * Providers:
18971
+ * - HUB: `ServerUpdateService` behind the `server-provided` mount
18972
+ * (`buildServerProviders` in trpc.router.ts) — the default target for
18973
+ * unpinned calls.
18974
+ * - AGENT: `AgentUpdateService` registered by the agent bootstrap under
18975
+ * the synthetic `agent-runtime` addonId and declared in the agent's
18976
+ * `$hub.registerNode` manifest.
18977
+ *
18978
+ * Node routing: singleton caps get the codegen/runtime-builder `nodeId`
18979
+ * injection on every method — `input.nodeId` (or `nodePin(nodeId)` from the
18980
+ * SDK) routes the call to that node's provider via the standard remote
18981
+ * proxy (`createCapabilityProxy` → `$agent-cap-fwd` → the agent's
18982
+ * in-process provider lookup). No `nodeId` → the hub's own provider.
18983
+ *
18984
+ * Spec: docs/superpowers/specs/2026-07-12-runtime-updatable-node-packages-design.md
18985
+ */
18986
+ /**
18987
+ * Where the running hub's code was loaded from:
18988
+ * - `workspace` — dev checkout (tsx / workspace dist); the starter defers to
18989
+ * plain resolution and runtime updates are refused.
18990
+ * - `baked` — the immutable image seed closure (no data-dir root active).
18991
+ * - `data-root` — the runtime-updatable `<dataDir>/server-root` closure.
18992
+ */
18993
+ var ServerBootModeSchema = _enum([
18994
+ "workspace",
18995
+ "baked",
18996
+ "data-root"
18997
+ ]);
18998
+ /**
18999
+ * Update lifecycle state:
19000
+ * - `idle` / `checking` / `staging` — steady / in-flight registry work.
19001
+ * - `pending-restart` — a version is staged and the node has NOT yet
19002
+ * restarted onto it (still running the OLD version).
19003
+ * - `awaiting-confirmation` — the node HAS restarted onto the staged version
19004
+ * (it is the active probation boot) and is waiting to confirm boot-health.
19005
+ * Apply/rollback are refused in this state and the node must NOT be
19006
+ * manually restarted, or the probation boot auto-rolls-back.
19007
+ */
19008
+ var ServerUpdateStateSchema = _enum([
19009
+ "idle",
19010
+ "checking",
19011
+ "staging",
19012
+ "pending-restart",
19013
+ "awaiting-confirmation"
19014
+ ]);
19015
+ var ServerRollbackInfoSchema = object({
19016
+ /** The version that failed (or was manually rolled back). */
19017
+ fromVersion: string(),
19018
+ /** The version rolled back to; null = the baked seed. */
19019
+ toVersion: string().nullable(),
19020
+ atMs: number(),
19021
+ reason: string()
18724
19022
  });
18725
- var ExposedResourceSchema = object({
18726
- streamId: string(),
18727
- format: string(),
18728
- value: string()
19023
+ var ServerPackageStatusSchema = object({
19024
+ /** Root package name (`@camstack/server` on the hub). */
19025
+ packageName: string(),
19026
+ /** Version of the code the running process ACTUALLY loaded. */
19027
+ runningVersion: string().nullable(),
19028
+ /** Node.js runtime version the node's process runs on (`process.versions.node`). */
19029
+ nodeRuntimeVersion: string().nullable(),
19030
+ /** Active data-dir root version; null when booted from seed/workspace. */
19031
+ activeVersion: string().nullable(),
19032
+ /** N-1 version kept for rollback; null when no previous version exists. */
19033
+ previousVersion: string().nullable(),
19034
+ /** Version of the immutable baked seed closure (image fallback). */
19035
+ seedVersion: string().nullable(),
19036
+ /** Latest registry version from the most recent check (null = never checked). */
19037
+ latestVersion: string().nullable(),
19038
+ updateAvailable: boolean(),
19039
+ bootMode: ServerBootModeSchema,
19040
+ updateState: ServerUpdateStateSchema,
19041
+ /** Version staged + awaiting its probation boot, when one is pending. */
19042
+ pendingVersion: string().nullable(),
19043
+ /** Set when the last freshly-activated version failed its boot health-check. */
19044
+ rolledBack: ServerRollbackInfoSchema.nullable(),
19045
+ /**
19046
+ * True when `server-root/state.json` EXISTS but is unreadable/corrupt — the
19047
+ * hub is running from the baked seed (or workspace) while installed data-dir
19048
+ * versions are being IGNORED. Surfaced as a warning in the UI.
19049
+ */
19050
+ stateFileCorrupt: boolean(),
19051
+ lastCheckedAtMs: number().nullable()
19052
+ });
19053
+ var ServerUpdateCheckResultSchema = object({
19054
+ packageName: string(),
19055
+ runningVersion: string().nullable(),
19056
+ latestVersion: string().nullable(),
19057
+ updateAvailable: boolean(),
19058
+ checkedAtMs: number(),
19059
+ /** Non-null when the registry lookup failed (offline, bad registry, …). */
19060
+ error: string().nullable()
19061
+ });
19062
+ var ServerUpdateActionResultSchema = object({
19063
+ accepted: boolean(),
19064
+ targetVersion: string().nullable(),
19065
+ /** True when a graceful restart was scheduled to apply the change. */
19066
+ restarting: boolean(),
19067
+ message: string()
19068
+ });
19069
+ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
19070
+ kind: "mutation",
19071
+ auth: "admin"
19072
+ }), method(object({
19073
+ /** Explicit target version; omitted = latest from the registry. */
19074
+ version: string().optional() }), ServerUpdateActionResultSchema, {
19075
+ kind: "mutation",
19076
+ auth: "admin"
19077
+ }), method(_void(), ServerUpdateActionResultSchema, {
19078
+ kind: "mutation",
19079
+ auth: "admin"
19080
+ }), method(_void(), ServerUpdateActionResultSchema, {
19081
+ kind: "mutation",
19082
+ auth: "admin"
18729
19083
  });
18730
- method(object({
18731
- deviceId: number(),
18732
- streams: array(RegisteredStreamSchema).readonly()
18733
- }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), array(ExposedResourceSchema).readonly());
18734
19084
  /**
18735
19085
  * Query filter for settings-store collections.
18736
19086
  */
@@ -18883,9 +19233,9 @@ method(SendEmailInputSchema, SendEmailResultSchema, {
18883
19233
  /**
18884
19234
  * A single device snapshot returned as base64 JPEG/PNG.
18885
19235
  *
18886
- * Shared with the `snapshot-provider` collection cap the orchestrator
18887
- * receives the same shape from each native provider and from the
18888
- * broker-based fallback.
19236
+ * The `SnapshotAddon` wrapper returns this shape whether the frame came from
19237
+ * the device-native provider (onboard capture) or from the stream-broker
19238
+ * prebuffer fallback.
18889
19239
  */
18890
19240
  var SnapshotImageSchema = object({
18891
19241
  base64: string(),
@@ -18953,17 +19303,26 @@ var snapshotCapability = {
18953
19303
  invalidateCache: method(object({ deviceId: number() }), _void(), {
18954
19304
  kind: "mutation",
18955
19305
  auth: "admin"
18956
- })
19306
+ }),
19307
+ /**
19308
+ * Cache-only batch overview — answers from the wrapper's in-memory cache in
19309
+ * O(n) and NEVER triggers a capture. Lets a grid skip requesting images for
19310
+ * devices that never produced a frame, and gives it an ETag per device for
19311
+ * conditional (304-able) image fetches. `lastCapturedAt`/`cacheAgeMs`/`etag`
19312
+ * are null for a device with no cached frame.
19313
+ */
19314
+ getSnapshotOverview: systemMethod(object({ deviceIds: array(number()).min(1).max(200) }), array(object({
19315
+ deviceId: number(),
19316
+ lastCapturedAt: number().nullable(),
19317
+ cacheAgeMs: number().nullable(),
19318
+ etag: string().nullable()
19319
+ })))
18957
19320
  },
18958
19321
  status: {
18959
19322
  schema: SnapshotStatusSchema,
18960
19323
  kind: "poll"
18961
19324
  }
18962
19325
  };
18963
- method(object({ deviceId: number() }), boolean()), method(object({
18964
- deviceId: number(),
18965
- streamId: string().optional()
18966
- }), SnapshotImageSchema.nullable());
18967
19326
  /**
18968
19327
  * `sso-bridge` — internal hub-only cap that lets SSO-style auth
18969
19328
  * providers (OIDC, SAML, magic-link, …) mint an HMAC-signed token
@@ -19214,10 +19573,32 @@ method(_void(), array(TurnServerSchema).readonly());
19214
19573
  * b. `finishAuthentication({userId, response})` → server verifies
19215
19574
  * the assertion, bumps the credential counter, returns ok.
19216
19575
  *
19576
+ * 2b. Usernameless (discoverable-credential) authentication — the
19577
+ * passkey IS the primary factor, no password leg:
19578
+ * a. `beginDiscoverableAuthentication({})` → assertion options with
19579
+ * EMPTY `allowCredentials` (the browser offers every resident
19580
+ * passkey it holds for this RP) + `userVerification: 'required'`
19581
+ * (the passkey replaces both factors, so UV is mandatory).
19582
+ * The challenge is stored server-side, NOT bound to any user.
19583
+ * b. `finishDiscoverableAuthentication({response})` → the provider
19584
+ * resolves the credential by the response's credential id,
19585
+ * verifies the assertion against the stored challenge + that
19586
+ * credential's public key/counter, and returns the OWNING
19587
+ * `userId` — the caller (core auth router) mints the session.
19588
+ *
19217
19589
  * 3. Management:
19218
19590
  * - `listPasskeys({userId})` — enumerate user's enrolled credentials.
19219
19591
  * - `removePasskey({userId, credentialId})` — revoke one credential.
19220
19592
  *
19593
+ * 4. Second-factor preference (opt-in, default OFF):
19594
+ * Enrolling a passkey only enables passkey-FIRST sign-in. It is
19595
+ * demanded as a second factor after a password login ONLY when the
19596
+ * user explicitly opts in via `setSecondFactorPreference`.
19597
+ * - `getSecondFactorPreference({userId})` → `{ enabled }` (missing
19598
+ * row ⇒ `enabled: false`).
19599
+ * - `setSecondFactorPreference({userId, enabled})` — persisted by
19600
+ * the providing addon beside its credentials.
19601
+ *
19221
19602
  * Challenges are short-lived (5 min, in-memory). The cap is internal —
19222
19603
  * the admin-ui composes the begin/finish round-trip and never exposes
19223
19604
  * the cap to non-admins.
@@ -19260,6 +19641,17 @@ method(object({
19260
19641
  }), object({ verified: boolean() }), {
19261
19642
  kind: "mutation",
19262
19643
  access: "view"
19644
+ }), method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
19645
+ kind: "mutation",
19646
+ access: "view"
19647
+ }), method(object({
19648
+ /** AuthenticationResponseJSON from the browser. */
19649
+ response: record(string(), unknown()) }), object({
19650
+ verified: boolean(),
19651
+ userId: string().nullable()
19652
+ }), {
19653
+ kind: "mutation",
19654
+ access: "view"
19263
19655
  }), method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }), method(object({
19264
19656
  userId: string(),
19265
19657
  credentialId: string()
@@ -19267,6 +19659,13 @@ method(object({
19267
19659
  kind: "mutation",
19268
19660
  auth: "admin",
19269
19661
  access: "delete"
19662
+ }), method(object({ userId: string() }), object({ enabled: boolean() }), { auth: "admin" }), method(object({
19663
+ userId: string(),
19664
+ enabled: boolean()
19665
+ }), object({ success: literal(true) }), {
19666
+ kind: "mutation",
19667
+ auth: "admin",
19668
+ access: "create"
19270
19669
  });
19271
19670
  /**
19272
19671
  * `videoclips` — the unified, navigable-clip surface for a camera.
@@ -19324,9 +19723,10 @@ method(object({
19324
19723
  auth: "admin"
19325
19724
  });
19326
19725
  /**
19327
- * Optional client-side hints sent at session creation to help the
19328
- * provider pick the best native source. All fields are optional —
19329
- * a viewer that knows nothing still gets a sane default.
19726
+ * Optional client-side hints sent at session creation to help the provider
19727
+ * pick the best native source. All fields optional — a viewer that knows
19728
+ * nothing still gets a sane default. (Relocated from the retired `webrtc`
19729
+ * collection cap; this `webrtc-session` cap is the live signaling surface.)
19330
19730
  */
19331
19731
  var webrtcClientHintsSchema = object({
19332
19732
  viewportWidth: number().int().positive().optional(),
@@ -19337,22 +19737,6 @@ var webrtcClientHintsSchema = object({
19337
19737
  /** Hard tier override; takes precedence over scoring when registered. */
19338
19738
  prefersTier: string().optional()
19339
19739
  }).partial();
19340
- method(object({
19341
- streamId: string(),
19342
- sdpOffer: string()
19343
- }), string(), { kind: "mutation" }), method(object({ streamId: string() }), boolean()), method(object({
19344
- streamId: string(),
19345
- codec: string()
19346
- }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), _void(), { kind: "mutation" }), method(object({
19347
- streamId: string(),
19348
- hints: webrtcClientHintsSchema.optional()
19349
- }), object({
19350
- sessionId: string(),
19351
- sdpOffer: string()
19352
- }), { kind: "mutation" }), method(object({
19353
- sessionId: string(),
19354
- sdpAnswer: string()
19355
- }), _void(), { kind: "mutation" }), method(object({ sessionId: string() }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), boolean());
19356
19740
  /**
19357
19741
  * Discriminated target for a WebRTC session. The client sends this
19358
19742
  * structured object instead of building / parsing brokerId strings;
@@ -20083,7 +20467,17 @@ var FaceInfoSchema = object({
20083
20467
  recognizedIdentityId: string().optional(),
20084
20468
  identityName: string().optional(),
20085
20469
  assigned: boolean(),
20086
- base64: string().optional()
20470
+ base64: string().optional(),
20471
+ /** Design B: the face bbox (pixel space) on the key frame — lets a detail
20472
+ * view draw the box over the native `keyFrameMediaKey` frame. Absent on
20473
+ * legacy rows written before design B. */
20474
+ faceBbox: BoundingBoxSchema.optional(),
20475
+ /** Design B: MediaStore key of the track's native-resolution key frame.
20476
+ * Fetch the native JPEG via the event-media data-plane
20477
+ * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
20478
+ * track produced no key frame (e.g. native/onboard source) — the UI falls
20479
+ * back to the inline `base64` face crop. */
20480
+ keyFrameMediaKey: string().optional()
20087
20481
  });
20088
20482
  var FaceFilterEnum = _enum([
20089
20483
  "unassigned",
@@ -20831,6 +21225,16 @@ var TopologyCategorySchema = object({
20831
21225
  healthy: number(),
20832
21226
  addons: array(TopologyCategoryAddonSchema).readonly()
20833
21227
  });
21228
+ /**
21229
+ * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
21230
+ * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
21231
+ * version visibility for the Server management surface. Nullable: offline
21232
+ * rows and pre-phase-2 nodes report none.
21233
+ */
21234
+ var TopologyRootPackageSchema = object({
21235
+ name: string(),
21236
+ version: string()
21237
+ });
20834
21238
  var TopologyNodeSchema = object({
20835
21239
  id: string(),
20836
21240
  name: string(),
@@ -20854,7 +21258,8 @@ var TopologyNodeSchema = object({
20854
21258
  status: string()
20855
21259
  })).readonly(),
20856
21260
  processes: array(TopologyProcessSchema).readonly(),
20857
- categories: array(TopologyCategorySchema).readonly()
21261
+ categories: array(TopologyCategorySchema).readonly(),
21262
+ rootPackage: TopologyRootPackageSchema.nullable()
20858
21263
  });
20859
21264
  var CapUsageEdgeSchema = object({
20860
21265
  callerAddonId: string(),
@@ -23932,6 +24337,12 @@ Object.freeze({
23932
24337
  addonId: null,
23933
24338
  access: "create"
23934
24339
  },
24340
+ "loginMethod.getLoginMethods": {
24341
+ capName: "login-method",
24342
+ capScope: "system",
24343
+ addonId: null,
24344
+ access: "view"
24345
+ },
23935
24346
  "mediaPlayer.next": {
23936
24347
  capName: "media-player",
23937
24348
  capScope: "device",
@@ -24514,6 +24925,12 @@ Object.freeze({
24514
24925
  addonId: null,
24515
24926
  access: "view"
24516
24927
  },
24928
+ "pipelineAnalytics.getKeyEvents": {
24929
+ capName: "pipeline-analytics",
24930
+ capScope: "device",
24931
+ addonId: null,
24932
+ access: "view"
24933
+ },
24517
24934
  "pipelineAnalytics.getMotionEvents": {
24518
24935
  capName: "pipeline-analytics",
24519
24936
  capScope: "device",
@@ -24562,23 +24979,23 @@ Object.freeze({
24562
24979
  addonId: null,
24563
24980
  access: "create"
24564
24981
  },
24565
- "pipelineExecutor.deleteModel": {
24982
+ "pipelineExecutor.clearDeviceOverrides": {
24566
24983
  capName: "pipeline-executor",
24567
24984
  capScope: "system",
24568
24985
  addonId: null,
24569
24986
  access: "delete"
24570
24987
  },
24571
- "pipelineExecutor.deleteTemplate": {
24988
+ "pipelineExecutor.deleteModel": {
24572
24989
  capName: "pipeline-executor",
24573
24990
  capScope: "system",
24574
24991
  addonId: null,
24575
24992
  access: "delete"
24576
24993
  },
24577
- "pipelineExecutor.detect": {
24994
+ "pipelineExecutor.deleteTemplate": {
24578
24995
  capName: "pipeline-executor",
24579
24996
  capScope: "system",
24580
24997
  addonId: null,
24581
- access: "view"
24998
+ access: "delete"
24582
24999
  },
24583
25000
  "pipelineExecutor.downloadModel": {
24584
25001
  capName: "pipeline-executor",
@@ -24772,13 +25189,13 @@ Object.freeze({
24772
25189
  addonId: null,
24773
25190
  access: "create"
24774
25191
  },
24775
- "pipelineOrchestrator.assignAudio": {
24776
- capName: "pipeline-orchestrator",
25192
+ "pipelineExecutor.validatePipeline": {
25193
+ capName: "pipeline-executor",
24777
25194
  capScope: "system",
24778
25195
  addonId: null,
24779
- access: "create"
25196
+ access: "view"
24780
25197
  },
24781
- "pipelineOrchestrator.assignDecoder": {
25198
+ "pipelineOrchestrator.assignAudio": {
24782
25199
  capName: "pipeline-orchestrator",
24783
25200
  capScope: "system",
24784
25201
  addonId: null,
@@ -24862,19 +25279,13 @@ Object.freeze({
24862
25279
  addonId: null,
24863
25280
  access: "view"
24864
25281
  },
24865
- "pipelineOrchestrator.getDecoderAssignment": {
24866
- capName: "pipeline-orchestrator",
24867
- capScope: "system",
24868
- addonId: null,
24869
- access: "view"
24870
- },
24871
- "pipelineOrchestrator.getDecoderAssignments": {
25282
+ "pipelineOrchestrator.getGlobalMetrics": {
24872
25283
  capName: "pipeline-orchestrator",
24873
25284
  capScope: "system",
24874
25285
  addonId: null,
24875
25286
  access: "view"
24876
25287
  },
24877
- "pipelineOrchestrator.getGlobalMetrics": {
25288
+ "pipelineOrchestrator.getIngestOwner": {
24878
25289
  capName: "pipeline-orchestrator",
24879
25290
  capScope: "system",
24880
25291
  addonId: null,
@@ -24916,6 +25327,12 @@ Object.freeze({
24916
25327
  addonId: null,
24917
25328
  access: "delete"
24918
25329
  },
25330
+ "pipelineOrchestrator.resetNodePipelineDefaults": {
25331
+ capName: "pipeline-orchestrator",
25332
+ capScope: "system",
25333
+ addonId: null,
25334
+ access: "delete"
25335
+ },
24919
25336
  "pipelineOrchestrator.resolvePipeline": {
24920
25337
  capName: "pipeline-orchestrator",
24921
25338
  capScope: "system",
@@ -24952,37 +25369,37 @@ Object.freeze({
24952
25369
  addonId: null,
24953
25370
  access: "create"
24954
25371
  },
24955
- "pipelineOrchestrator.setCameraPipelineForAgent": {
25372
+ "pipelineOrchestrator.setAgentReachableHost": {
24956
25373
  capName: "pipeline-orchestrator",
24957
25374
  capScope: "system",
24958
25375
  addonId: null,
24959
25376
  access: "create"
24960
25377
  },
24961
- "pipelineOrchestrator.setCameraStepOverride": {
25378
+ "pipelineOrchestrator.setCameraPipelineForAgent": {
24962
25379
  capName: "pipeline-orchestrator",
24963
25380
  capScope: "system",
24964
25381
  addonId: null,
24965
25382
  access: "create"
24966
25383
  },
24967
- "pipelineOrchestrator.setCameraStepToggle": {
25384
+ "pipelineOrchestrator.setCameraStepOverride": {
24968
25385
  capName: "pipeline-orchestrator",
24969
25386
  capScope: "system",
24970
25387
  addonId: null,
24971
25388
  access: "create"
24972
25389
  },
24973
- "pipelineOrchestrator.setCapabilityBinding": {
25390
+ "pipelineOrchestrator.setCameraStepToggle": {
24974
25391
  capName: "pipeline-orchestrator",
24975
25392
  capScope: "system",
24976
25393
  addonId: null,
24977
25394
  access: "create"
24978
25395
  },
24979
- "pipelineOrchestrator.unassignAudio": {
25396
+ "pipelineOrchestrator.setCapabilityBinding": {
24980
25397
  capName: "pipeline-orchestrator",
24981
25398
  capScope: "system",
24982
25399
  addonId: null,
24983
25400
  access: "create"
24984
25401
  },
24985
- "pipelineOrchestrator.unassignDecoder": {
25402
+ "pipelineOrchestrator.unassignAudio": {
24986
25403
  capName: "pipeline-orchestrator",
24987
25404
  capScope: "system",
24988
25405
  addonId: null,
@@ -25042,6 +25459,12 @@ Object.freeze({
25042
25459
  addonId: null,
25043
25460
  access: "view"
25044
25461
  },
25462
+ "pipelineRunner.getNativeCrop": {
25463
+ capName: "pipeline-runner",
25464
+ capScope: "system",
25465
+ addonId: null,
25466
+ access: "view"
25467
+ },
25045
25468
  "pipelineRunner.reportMotion": {
25046
25469
  capName: "pipeline-runner",
25047
25470
  capScope: "system",
@@ -25282,33 +25705,45 @@ Object.freeze({
25282
25705
  addonId: null,
25283
25706
  access: "create"
25284
25707
  },
25285
- "restreamer.getExposedResources": {
25286
- capName: "restreamer",
25708
+ "scriptRunner.run": {
25709
+ capName: "script-runner",
25710
+ capScope: "device",
25711
+ addonId: null,
25712
+ access: "create"
25713
+ },
25714
+ "scriptRunner.stop": {
25715
+ capName: "script-runner",
25716
+ capScope: "device",
25717
+ addonId: null,
25718
+ access: "create"
25719
+ },
25720
+ "serverManagement.applyServerUpdate": {
25721
+ capName: "server-management",
25287
25722
  capScope: "system",
25288
25723
  addonId: null,
25289
- access: "view"
25724
+ access: "create"
25290
25725
  },
25291
- "restreamer.registerDevice": {
25292
- capName: "restreamer",
25726
+ "serverManagement.checkServerUpdate": {
25727
+ capName: "server-management",
25293
25728
  capScope: "system",
25294
25729
  addonId: null,
25295
25730
  access: "create"
25296
25731
  },
25297
- "restreamer.unregisterDevice": {
25298
- capName: "restreamer",
25732
+ "serverManagement.getServerPackageStatus": {
25733
+ capName: "server-management",
25299
25734
  capScope: "system",
25300
25735
  addonId: null,
25301
- access: "delete"
25736
+ access: "view"
25302
25737
  },
25303
- "scriptRunner.run": {
25304
- capName: "script-runner",
25305
- capScope: "device",
25738
+ "serverManagement.restartServer": {
25739
+ capName: "server-management",
25740
+ capScope: "system",
25306
25741
  addonId: null,
25307
25742
  access: "create"
25308
25743
  },
25309
- "scriptRunner.stop": {
25310
- capName: "script-runner",
25311
- capScope: "device",
25744
+ "serverManagement.rollbackServerUpdate": {
25745
+ capName: "server-management",
25746
+ capScope: "system",
25312
25747
  addonId: null,
25313
25748
  access: "create"
25314
25749
  },
@@ -25396,23 +25831,17 @@ Object.freeze({
25396
25831
  addonId: null,
25397
25832
  access: "view"
25398
25833
  },
25399
- "snapshot.invalidateCache": {
25834
+ "snapshot.getSnapshotOverview": {
25400
25835
  capName: "snapshot",
25401
25836
  capScope: "device",
25402
25837
  addonId: null,
25403
- access: "create"
25404
- },
25405
- "snapshotProvider.getSnapshot": {
25406
- capName: "snapshot-provider",
25407
- capScope: "system",
25408
- addonId: null,
25409
25838
  access: "view"
25410
25839
  },
25411
- "snapshotProvider.supportsDevice": {
25412
- capName: "snapshot-provider",
25413
- capScope: "system",
25840
+ "snapshot.invalidateCache": {
25841
+ capName: "snapshot",
25842
+ capScope: "device",
25414
25843
  addonId: null,
25415
- access: "view"
25844
+ access: "create"
25416
25845
  },
25417
25846
  "ssoBridge.signBridgeToken": {
25418
25847
  capName: "sso-bridge",
@@ -25840,30 +26269,6 @@ Object.freeze({
25840
26269
  addonId: null,
25841
26270
  access: "view"
25842
26271
  },
25843
- "streamingEngine.getStreamUrl": {
25844
- capName: "streaming-engine",
25845
- capScope: "system",
25846
- addonId: null,
25847
- access: "view"
25848
- },
25849
- "streamingEngine.listStreams": {
25850
- capName: "streaming-engine",
25851
- capScope: "system",
25852
- addonId: null,
25853
- access: "view"
25854
- },
25855
- "streamingEngine.registerStream": {
25856
- capName: "streaming-engine",
25857
- capScope: "system",
25858
- addonId: null,
25859
- access: "create"
25860
- },
25861
- "streamingEngine.unregisterStream": {
25862
- capName: "streaming-engine",
25863
- capScope: "system",
25864
- addonId: null,
25865
- access: "delete"
25866
- },
25867
26272
  "streamParams.getConfigSchema": {
25868
26273
  capName: "stream-params",
25869
26274
  capScope: "device",
@@ -26110,6 +26515,12 @@ Object.freeze({
26110
26515
  addonId: null,
26111
26516
  access: "view"
26112
26517
  },
26518
+ "userPasskeys.beginDiscoverableAuthentication": {
26519
+ capName: "user-passkeys",
26520
+ capScope: "system",
26521
+ addonId: null,
26522
+ access: "view"
26523
+ },
26113
26524
  "userPasskeys.beginRegistration": {
26114
26525
  capName: "user-passkeys",
26115
26526
  capScope: "system",
@@ -26122,12 +26533,24 @@ Object.freeze({
26122
26533
  addonId: null,
26123
26534
  access: "view"
26124
26535
  },
26536
+ "userPasskeys.finishDiscoverableAuthentication": {
26537
+ capName: "user-passkeys",
26538
+ capScope: "system",
26539
+ addonId: null,
26540
+ access: "view"
26541
+ },
26125
26542
  "userPasskeys.finishRegistration": {
26126
26543
  capName: "user-passkeys",
26127
26544
  capScope: "system",
26128
26545
  addonId: null,
26129
26546
  access: "create"
26130
26547
  },
26548
+ "userPasskeys.getSecondFactorPreference": {
26549
+ capName: "user-passkeys",
26550
+ capScope: "system",
26551
+ addonId: null,
26552
+ access: "view"
26553
+ },
26131
26554
  "userPasskeys.listPasskeys": {
26132
26555
  capName: "user-passkeys",
26133
26556
  capScope: "system",
@@ -26140,6 +26563,12 @@ Object.freeze({
26140
26563
  addonId: null,
26141
26564
  access: "delete"
26142
26565
  },
26566
+ "userPasskeys.setSecondFactorPreference": {
26567
+ capName: "user-passkeys",
26568
+ capScope: "system",
26569
+ addonId: null,
26570
+ access: "create"
26571
+ },
26143
26572
  "vacuumControl.locate": {
26144
26573
  capName: "vacuum-control",
26145
26574
  capScope: "device",
@@ -26212,6 +26641,18 @@ Object.freeze({
26212
26641
  addonId: null,
26213
26642
  access: "view"
26214
26643
  },
26644
+ "viewerUi.getStaticDir": {
26645
+ capName: "viewer-ui",
26646
+ capScope: "system",
26647
+ addonId: null,
26648
+ access: "view"
26649
+ },
26650
+ "viewerUi.getVersion": {
26651
+ capName: "viewer-ui",
26652
+ capScope: "system",
26653
+ addonId: null,
26654
+ access: "view"
26655
+ },
26215
26656
  "waterHeater.setAway": {
26216
26657
  capName: "water-heater",
26217
26658
  capScope: "device",
@@ -26230,54 +26671,6 @@ Object.freeze({
26230
26671
  addonId: null,
26231
26672
  access: "create"
26232
26673
  },
26233
- "webrtc.closeSession": {
26234
- capName: "webrtc",
26235
- capScope: "system",
26236
- addonId: null,
26237
- access: "create"
26238
- },
26239
- "webrtc.createSession": {
26240
- capName: "webrtc",
26241
- capScope: "system",
26242
- addonId: null,
26243
- access: "create"
26244
- },
26245
- "webrtc.handleAnswer": {
26246
- capName: "webrtc",
26247
- capScope: "system",
26248
- addonId: null,
26249
- access: "create"
26250
- },
26251
- "webrtc.handleOffer": {
26252
- capName: "webrtc",
26253
- capScope: "system",
26254
- addonId: null,
26255
- access: "create"
26256
- },
26257
- "webrtc.hasAdaptiveBitrate": {
26258
- capName: "webrtc",
26259
- capScope: "system",
26260
- addonId: null,
26261
- access: "view"
26262
- },
26263
- "webrtc.registerStream": {
26264
- capName: "webrtc",
26265
- capScope: "system",
26266
- addonId: null,
26267
- access: "create"
26268
- },
26269
- "webrtc.supportsStream": {
26270
- capName: "webrtc",
26271
- capScope: "system",
26272
- addonId: null,
26273
- access: "view"
26274
- },
26275
- "webrtc.unregisterStream": {
26276
- capName: "webrtc",
26277
- capScope: "system",
26278
- addonId: null,
26279
- access: "delete"
26280
- },
26281
26674
  "webrtcSession.addIceCandidate": {
26282
26675
  capName: "webrtc-session",
26283
26676
  capScope: "device",