@camstack/addon-import-alexa 0.1.17 → 0.1.19

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 +715 -288
  2. package/dist/addon.mjs +715 -288
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -4681,7 +4681,7 @@ function preprocess(fn, schema) {
4681
4681
  });
4682
4682
  }
4683
4683
  //#endregion
4684
- //#region ../types/dist/sleep-CZDdRBua.mjs
4684
+ //#region ../types/dist/sleep-Baang_XW.mjs
4685
4685
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4686
4686
  EventCategory["SystemBoot"] = "system.boot";
4687
4687
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -4867,6 +4867,18 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
4867
4867
  */
4868
4868
  EventCategory["PipelineCameraUpdated"] = "pipeline.camera-updated";
4869
4869
  /**
4870
+ * The cluster camera-source OWNER changed (`clusterRoles.ingestNode`).
4871
+ * Emitted by addon-pipeline-orchestrator whenever it (re)derives node
4872
+ * capabilities — at boot, on agent online/offline, and on an ingest-node
4873
+ * flip. Carries the resolved `ownerNodeId`. The stream-broker consumes it to
4874
+ * keep its ingest-owner-gate decision current WITHOUT a per-`ensureBroker`
4875
+ * cross-process `getIngestOwner` query (push the authority's decision instead
4876
+ * of polling it on the hot path). Idempotent state — re-emitted on every
4877
+ * topology change, so a dropped event self-heals on the next one (plus the
4878
+ * broker's long backstop reconcile query).
4879
+ */
4880
+ EventCategory["PipelineIngestOwnerChanged"] = "pipeline.ingest-owner-changed";
4881
+ /**
4870
4882
  * Periodic snapshot of per-node pipeline-runner load
4871
4883
  * (`RunnerLocalLoad`). Emitted ~1Hz by every runner so UI dashboards
4872
4884
  * subscribe instead of polling `pipelineRunner.getLocalLoad`.
@@ -5390,10 +5402,6 @@ function hydrateField(field, values) {
5390
5402
  };
5391
5403
  }
5392
5404
  const rawValue = storedValue !== void 0 ? storedValue : defaultValue !== void 0 ? defaultValue : null;
5393
- if (field.type === "password") return {
5394
- ...field,
5395
- value: ""
5396
- };
5397
5405
  const value = field.type === "textarea" && field.isJson && rawValue !== null && typeof rawValue === "object" ? JSON.stringify(rawValue, null, 2) : rawValue;
5398
5406
  return {
5399
5407
  ...field,
@@ -6777,10 +6785,25 @@ function method(input, output, options) {
6777
6785
  timeoutMs: options?.timeoutMs
6778
6786
  };
6779
6787
  }
6788
+ /**
6789
+ * A wrapper/system-only method: served exclusively by the cap's system-level
6790
+ * provider (`InferProvider`), and OPTIONAL on `InferNativeProvider` so per-device
6791
+ * driver natives don't stub out a wrapper concern (e.g. a cross-device cache
6792
+ * overview). The `systemOnly: true` literal is what `InferNativeProvider` keys on.
6793
+ */
6794
+ function systemMethod(input, output, options) {
6795
+ return {
6796
+ ...method(input, output, options),
6797
+ systemOnly: true
6798
+ };
6799
+ }
6780
6800
  /** Shorthand to define an event schema */
6781
6801
  function event(data) {
6782
6802
  return { data };
6783
6803
  }
6804
+ var StaticDirOutputSchema$1 = object({ staticDir: string() });
6805
+ var VersionOutputSchema$1 = object({ version: string() });
6806
+ method(_void(), StaticDirOutputSchema$1), method(_void(), VersionOutputSchema$1);
6784
6807
  var StaticDirOutputSchema = object({ staticDir: string() });
6785
6808
  var VersionOutputSchema = object({ version: string() });
6786
6809
  method(_void(), StaticDirOutputSchema), method(_void(), VersionOutputSchema);
@@ -6962,6 +6985,36 @@ var ModelFormatsSchema = object({
6962
6985
  tflite: ModelFormatEntrySchema.optional(),
6963
6986
  pt: ModelFormatEntrySchema.optional()
6964
6987
  });
6988
+ /**
6989
+ * Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
6990
+ * the reduced `PipelineModelOption` returned in `pipeline.getSchema()` so the
6991
+ * grouped Family→Tier→Variant picker renders identically in the config UI and
6992
+ * in the pipeline/device steppers. The flat `id` stays the source of truth for
6993
+ * resolution/download/persistence; this is a presentation overlay resolved back
6994
+ * to an `id`.
6995
+ */
6996
+ var ModelVariantGroupSchema = object({
6997
+ /** Top-level family, e.g. `yolo26` (later `d-fine`, `rf-detr`). */
6998
+ family: string(),
6999
+ /** Size within the family, e.g. `n` | `s` | `m` | `l`. */
7000
+ tier: string(),
7001
+ /** Quantization axis. Omit ⇒ the fp32 base build. */
7002
+ precision: _enum(["fp32", "int8"]).optional(),
7003
+ /**
7004
+ * Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
7005
+ * latency-optimized export (e.g. ReLU-activation variant) — the slot the
7006
+ * future performance variants plug into.
7007
+ */
7008
+ optimization: _enum(["standard", "fast"]).optional(),
7009
+ /**
7010
+ * Input-resolution axis (square input side, px). Omit ⇒ the family's native
7011
+ * resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
7012
+ * cheap latency lever — especially on Apple ANE and the Intel N100 — at a
7013
+ * small-object accuracy cost. Mirrors the model's `inputSize` but lifted onto
7014
+ * the group so the selector can offer it as a variant axis.
7015
+ */
7016
+ resolution: number().int().positive().optional()
7017
+ });
6965
7018
  var ModelCatalogEntrySchema = object({
6966
7019
  id: string(),
6967
7020
  name: string(),
@@ -6991,7 +7044,43 @@ var ModelCatalogEntrySchema = object({
6991
7044
  * Auxiliary files required at runtime (labels JSON, charset dict, etc.).
6992
7045
  * Downloaded into the same modelsDir alongside the model file.
6993
7046
  */
6994
- extraFiles: array(ModelExtraFileSchema).readonly().optional()
7047
+ extraFiles: array(ModelExtraFileSchema).readonly().optional(),
7048
+ /**
7049
+ * LEGACY entry — retained in the catalog so a persisted operator selection
7050
+ * still RESOLVES (and can be re-activated), but hidden from the selectable
7051
+ * model list and excluded from the auto format-default pick. Set on the
7052
+ * superseded / consolidated models (older lineages, redundant fp16 IRs) so
7053
+ * the active lineup stays the coherent curated ladder without deleting a
7054
+ * model anyone may still be pinned to. `resolveModelForFormat` keeps honoring
7055
+ * an explicit legacy id that has a build for the node's format.
7056
+ */
7057
+ legacy: boolean().optional(),
7058
+ /**
7059
+ * Measured quality/latency metadata — populated from the benchmark addon on
7060
+ * the real node classes. Absent = not yet measured (most entries today; the
7061
+ * catalog historically carried only `sizeMB`, a poor cross-architecture
7062
+ * speed proxy). `p95LatencyMs` is keyed by node class (e.g. `n100`, `mac`).
7063
+ */
7064
+ metrics: object({
7065
+ map50: number().optional(),
7066
+ p95LatencyMs: record(string(), number()).optional()
7067
+ }).optional(),
7068
+ /**
7069
+ * SPDX-ish license id of the model weights (e.g. `AGPL-3.0` for Ultralytics
7070
+ * YOLO26, `GPL-3.0` for YOLOv9, `Apache-2.0` for D-FINE/RF-DETR). Matters for
7071
+ * the retraining addon and any future commercial distribution.
7072
+ */
7073
+ license: string().optional(),
7074
+ /**
7075
+ * Variant-selector grouping. The UI groups models by `family` + `tier` and
7076
+ * offers `precision` / `optimization` as variant axes WITHIN a tier — so all
7077
+ * of a family's sizes and quantizations collapse into one grouped picker
7078
+ * instead of a flat list of `yolo26s`, `yolo26s-int8`, … Absent ⇒ ungrouped
7079
+ * (legacy / custom models) — never shown in the grouped selector. The flat
7080
+ * `id` stays the source of truth for resolution/download/persistence; grouping
7081
+ * is a presentation overlay resolved back to an `id`.
7082
+ */
7083
+ group: ModelVariantGroupSchema.optional()
6995
7084
  });
6996
7085
  var ConvertTargetSchema = discriminatedUnion("format", [object({
6997
7086
  format: literal("openvino"),
@@ -7169,8 +7258,8 @@ var RecordingModeSchema = _enum([
7169
7258
  "onAudioThreshold"
7170
7259
  ]);
7171
7260
  /**
7172
- * First-class, authoritative per-camera storage mode — the netta choice the UI
7173
- * reads directly (never inferred from `rules`):
7261
+ * First-class, authoritative per-camera storage mode — the explicit choice the
7262
+ * UI reads directly (never inferred from `rules`):
7174
7263
  * - `off` — not recording.
7175
7264
  * - `events` — record only around triggers (motion / audio threshold),
7176
7265
  * with pre/post-buffer.
@@ -9333,26 +9422,13 @@ onBrightnessChanged: { data: object({
9333
9422
  */
9334
9423
  runtimeState: BrightnessStatusSchema
9335
9424
  };
9425
+ /** Stream delivery format. (Relocated from the retired `streaming-engine` cap.) */
9336
9426
  var StreamFormatSchema = _enum([
9337
9427
  "webrtc",
9338
9428
  "hls",
9339
9429
  "mjpeg",
9340
9430
  "rtsp"
9341
9431
  ]);
9342
- var StreamInfoSchema = object({
9343
- streamId: string(),
9344
- format: StreamFormatSchema,
9345
- url: string().nullable(),
9346
- active: boolean()
9347
- });
9348
- method(object({
9349
- streamId: string(),
9350
- sourceUrl: string(),
9351
- codec: string().optional()
9352
- }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), _void(), { kind: "mutation" }), method(object({
9353
- streamId: string(),
9354
- format: StreamFormatSchema
9355
- }), string().nullable()), method(_void(), array(StreamInfoSchema));
9356
9432
  var RtspRestreamEntrySchema = object({
9357
9433
  brokerId: string(),
9358
9434
  url: string(),
@@ -10220,37 +10296,7 @@ var consumablesCapability = {
10220
10296
  scope: "device",
10221
10297
  deviceNative: true,
10222
10298
  mode: "singleton",
10223
- deviceTypes: [
10224
- DeviceType.Camera,
10225
- DeviceType.Hub,
10226
- DeviceType.Light,
10227
- DeviceType.Siren,
10228
- DeviceType.Switch,
10229
- DeviceType.Sensor,
10230
- DeviceType.Thermostat,
10231
- DeviceType.Button,
10232
- DeviceType.EventEmitter,
10233
- DeviceType.Update,
10234
- DeviceType.Generic,
10235
- DeviceType.Notifier,
10236
- DeviceType.Script,
10237
- DeviceType.Automation,
10238
- DeviceType.Lock,
10239
- DeviceType.Cover,
10240
- DeviceType.Valve,
10241
- DeviceType.Humidifier,
10242
- DeviceType.WaterHeater,
10243
- DeviceType.Fan,
10244
- DeviceType.MediaPlayer,
10245
- DeviceType.AlarmPanel,
10246
- DeviceType.Control,
10247
- DeviceType.Presence,
10248
- DeviceType.Weather,
10249
- DeviceType.Vacuum,
10250
- DeviceType.LawnMower,
10251
- DeviceType.Container,
10252
- DeviceType.Image
10253
- ],
10299
+ deviceTypes: Object.values(DeviceType),
10254
10300
  deviceConfig: { ui: {
10255
10301
  kind: "widget",
10256
10302
  widgetId: "host/consumables-panel",
@@ -11708,7 +11754,7 @@ var BoundingBoxSchema = object({
11708
11754
  w: number(),
11709
11755
  h: number()
11710
11756
  });
11711
- var SpatialDetectionSchema = object({
11757
+ object({
11712
11758
  class: string(),
11713
11759
  originalClass: string(),
11714
11760
  score: number(),
@@ -11843,7 +11889,6 @@ var PipelineDefaultStepSchema = lazy(() => object({
11843
11889
  enabled: boolean(),
11844
11890
  modelId: string(),
11845
11891
  children: array(PipelineDefaultStepSchema).readonly(),
11846
- engine: PipelineEngineChoiceSchema.optional(),
11847
11892
  group: string().optional(),
11848
11893
  settings: record(string(), unknown()).optional()
11849
11894
  }));
@@ -11868,7 +11913,9 @@ var PipelineModelOptionSchema = object({
11868
11913
  formats: record(string(), object({
11869
11914
  downloaded: boolean(),
11870
11915
  sizeMB: number()
11871
- }))
11916
+ })),
11917
+ group: ModelVariantGroupSchema.optional(),
11918
+ legacy: boolean().optional()
11872
11919
  });
11873
11920
  var ConfigFieldBridge = custom();
11874
11921
  var PipelineAddonSchemaSchema = object({
@@ -11882,6 +11929,7 @@ var PipelineAddonSchemaSchema = object({
11882
11929
  defaultModelId: string(),
11883
11930
  defaultModelIdByFormat: record(string(), string()).optional(),
11884
11931
  enabledByDefault: boolean().optional(),
11932
+ backfillIntoExistingOverrides: boolean().optional(),
11885
11933
  defaultConfidence: number(),
11886
11934
  group: string().optional(),
11887
11935
  configSchema: array(ConfigFieldBridge).readonly().optional()
@@ -11898,11 +11946,6 @@ var PipelineSchemaSchema = object({
11898
11946
  selectedEngine: PipelineEngineChoiceSchema,
11899
11947
  slots: array(PipelineSlotSchemaSchema).readonly()
11900
11948
  });
11901
- var DetectorOutputSchema = object({
11902
- detections: array(SpatialDetectionSchema).readonly(),
11903
- inferenceMs: number(),
11904
- modelId: string()
11905
- });
11906
11949
  var EngineProvisioningSchema = object({
11907
11950
  runtimeId: _enum([
11908
11951
  "onnx",
@@ -11919,15 +11962,42 @@ var EngineProvisioningSchema = object({
11919
11962
  ]),
11920
11963
  progress: number().optional(),
11921
11964
  error: string().optional(),
11922
- nextRetryAt: number().optional()
11965
+ nextRetryAt: number().optional(),
11966
+ /**
11967
+ * Gate A (config-correctness gate at engine change): human-readable
11968
+ * config issues surfaced EAGERLY when the node's engine changes — model
11969
+ * substitutions ("chose X, running Y") and zero-build steps ("no model
11970
+ * has a <format> build"). Additive/optional: informational only, never
11971
+ * enforced here — `assertEngineReady` (readiness) still gates inference.
11972
+ * Absent/empty when the node-default tree resolves cleanly.
11973
+ */
11974
+ configIssues: array(string()).optional()
11923
11975
  });
11924
11976
  var PipelineStepInputSchema = lazy(() => object({
11925
11977
  addonId: string(),
11926
- modelId: string(),
11978
+ modelId: string().optional(),
11927
11979
  enabled: boolean().default(true),
11928
11980
  children: array(PipelineStepInputSchema).optional(),
11929
11981
  settings: record(string(), unknown()).optional()
11930
11982
  }));
11983
+ var ModelSubstitutionSchema = object({
11984
+ addonId: string(),
11985
+ chosen: string(),
11986
+ running: string(),
11987
+ format: string()
11988
+ });
11989
+ var PipelineValidationIssueSchema = object({
11990
+ addonId: string(),
11991
+ kind: _enum(["unknown-addon", "no-format-build"]),
11992
+ detail: string()
11993
+ });
11994
+ var PipelineValidationResultSchema = object({
11995
+ ok: boolean(),
11996
+ issues: array(PipelineValidationIssueSchema).readonly(),
11997
+ substitutions: array(ModelSubstitutionSchema).readonly(),
11998
+ /** The node's `currentEngine.format` this validation ran against. */
11999
+ format: string()
12000
+ });
11931
12001
  var ReferenceImageEntrySchema = object({
11932
12002
  filename: string(),
11933
12003
  stepIds: array(string()).readonly().optional()
@@ -11998,7 +12068,13 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11998
12068
  })) }), object({ success: literal(true) }), {
11999
12069
  kind: "mutation",
12000
12070
  auth: "admin"
12001
- }), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
12071
+ }), method(object({ nodeId: string() }), object({
12072
+ success: literal(true),
12073
+ clearedDevices: number()
12074
+ }), {
12075
+ kind: "mutation",
12076
+ auth: "admin"
12077
+ }), 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({
12002
12078
  name: string(),
12003
12079
  steps: array(PipelineTemplateStepSchema).readonly(),
12004
12080
  engine: PipelineEngineChoiceSchema
@@ -12015,10 +12091,6 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12015
12091
  modelId: string(),
12016
12092
  format: ModelFormatSchema$1
12017
12093
  }), object({ success: literal(true) }), { kind: "mutation" }), method(object({
12018
- addonId: string(),
12019
- frame: FrameInputSchema,
12020
- config: record(string(), unknown()).optional()
12021
- }), DetectorOutputSchema), method(object({
12022
12094
  engine: PipelineEngineChoiceSchema.optional(),
12023
12095
  steps: array(PipelineStepInputSchema).min(1),
12024
12096
  frame: FrameInputSchema.optional(),
@@ -12039,7 +12111,15 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12039
12111
  image: _instanceof(Uint8Array).optional(),
12040
12112
  referenceImage: string().optional(),
12041
12113
  deviceId: number().optional(),
12042
- sessionId: string().optional()
12114
+ sessionId: string().optional(),
12115
+ /**
12116
+ * Execution plane. 'full' (default) runs the whole tree — benchmark,
12117
+ * reference-image, and detail-subtree calls. 'frame' is the live
12118
+ * per-frame dispatch: ONLY root-plane steps run; crop children
12119
+ * (inputClasses ≠ null) are skipped and served per-track via
12120
+ * pipelineRunner.runDetailSubtree (two-plane design).
12121
+ */
12122
+ plane: _enum(["full", "frame"]).optional()
12043
12123
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12044
12124
  engine: PipelineEngineChoiceSchema.optional(),
12045
12125
  steps: array(PipelineStepInputSchema).min(1),
@@ -12197,6 +12277,47 @@ var zonesCapability = {
12197
12277
  runtimeState: object({ zones: array(ZoneSchema).readonly() })
12198
12278
  };
12199
12279
  /**
12280
+ * A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
12281
+ * decode worker resolves it against the RETAINED native frame's real pixel dims,
12282
+ * so the caller supplies only the detection-res bbox divided by the detection
12283
+ * dims — no native resolution to plumb.
12284
+ */
12285
+ var NativeCropBboxSchema = object({
12286
+ x: number(),
12287
+ y: number(),
12288
+ w: number(),
12289
+ h: number()
12290
+ });
12291
+ /** Result of a best-effort native-resolution crop (`getNativeCrop`). */
12292
+ var NativeCropResultSchema = object({
12293
+ /** Packed rgb (24-bit) pixels of the crop. */
12294
+ bytes: _instanceof(Uint8Array),
12295
+ width: number().int().positive(),
12296
+ height: number().int().positive()
12297
+ });
12298
+ /** Parent detection context passed to `runDetailSubtree` — the crop's
12299
+ * originating detection, in FRAME-space coordinates. Reuses
12300
+ * `NativeCropBboxSchema`'s `{x,y,w,h}` shape (same numeric fields; here
12301
+ * the coordinates are frame-space rather than getNativeCrop's
12302
+ * normalized [0,1] convention). */
12303
+ var DetailParentSchema = object({
12304
+ bbox: NativeCropBboxSchema,
12305
+ className: string()
12306
+ });
12307
+ /** One child-step result from `runDetailSubtree` — an embedding, label,
12308
+ * or refined detection produced by running the crop-subtree on a
12309
+ * single tracked detection. */
12310
+ var DetailResultSchema = object({
12311
+ stepId: string(),
12312
+ className: string(),
12313
+ score: number(),
12314
+ /** FRAME-space bbox (already mapped back from crop space). */
12315
+ bbox: NativeCropBboxSchema.optional(),
12316
+ embedding: string().optional(),
12317
+ label: string().optional(),
12318
+ alignedCropJpeg: string().optional()
12319
+ });
12320
+ /**
12200
12321
  * Per-camera tunable ranges + defaults. Single source of truth used
12201
12322
  * by both the Zod data schema (validation + default fallback) and
12202
12323
  * the device settings UI (slider min/max/step). Touch one place and
@@ -12291,6 +12412,13 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
12291
12412
  kind: literal("remote-restream"),
12292
12413
  /** The camera's source-owner node (slice 1: always the hub). */
12293
12414
  ownerNodeId: string(),
12415
+ /**
12416
+ * The owner's LAN-reachable host, resolved by the orchestrator from the
12417
+ * per-node `reachableHost` override (Cluster UI). When present the runner
12418
+ * dials THIS host for the owner's restream, in preference to the
12419
+ * `CAMSTACK_HUB_URL`-derived default. Absent → auto-detect fallback.
12420
+ */
12421
+ ownerReachableHost: string().optional(),
12294
12422
  /** Operator override for the owner host the runner dials. */
12295
12423
  hubHostnameOverride: string().optional()
12296
12424
  })]).describe("Per-camera frame-source mode for the runner (P2c)");
@@ -12299,13 +12427,11 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
12299
12427
  * specific runner instance via `attachCamera`. Carries everything the
12300
12428
  * runner needs to subscribe to the local broker and execute inference.
12301
12429
  *
12302
- * Stateless-pipeline model: the full pipeline content (`engine`, `steps`,
12303
- * optional `audio`) travels with the attach payload. The runner keeps it
12304
- * in RAM for the lifetime of the attach — on rebalance, edit, or
12305
- * restart the orchestrator re-sends the latest snapshot.
12306
- *
12307
- * `engine`/`steps`/`audio` are optional during the additive migration
12308
- * window; once orchestrator + UI are migrated they become required.
12430
+ * Stateless-pipeline model: the pipeline content (`steps`, optional
12431
+ * `audio`) travels with the attach payload. The runner keeps it in RAM
12432
+ * for the lifetime of the attach — on rebalance, edit, or restart the
12433
+ * orchestrator re-sends the latest snapshot. Engine is NOT carried: it is
12434
+ * node-local, resolved by the executing runner at dispatch time.
12309
12435
  */
12310
12436
  var RunnerCameraConfigSchema = object({
12311
12437
  deviceId: number(),
@@ -12356,14 +12482,11 @@ var RunnerCameraConfigSchema = object({
12356
12482
  */
12357
12483
  motionSources: MotionSourcesSchema.default(["analyzer"]),
12358
12484
  pipelineEnabled: boolean().default(true),
12359
- /** Engine choice for video steps (runtime+backend+format). */
12360
- engine: PipelineEngineChoiceSchema.optional(),
12361
12485
  /** Ordered tree of video steps. Absent → runner skips video detection. */
12362
12486
  steps: array(PipelineStepInputSchema).readonly().optional(),
12363
12487
  /** Audio classification branch. `enabled:false` disables, null skips. */
12364
12488
  audio: object({
12365
- engine: PipelineEngineChoiceSchema,
12366
- modelId: string(),
12489
+ modelId: string().optional(),
12367
12490
  enabled: boolean()
12368
12491
  }).nullable().optional(),
12369
12492
  /**
@@ -12450,7 +12573,17 @@ var RunnerLocalMetricsSchema = object({
12450
12573
  avgInferenceTimeMs: number(),
12451
12574
  queueDepth: number()
12452
12575
  });
12453
- 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());
12576
+ 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({
12577
+ handle: FrameHandleSchema,
12578
+ bbox: NativeCropBboxSchema,
12579
+ maxWidth: number().int().positive().optional()
12580
+ }), NativeCropResultSchema.nullable()), method(object({
12581
+ deviceId: number(),
12582
+ frameHandle: FrameHandleSchema.optional(),
12583
+ cropJpeg: string().optional(),
12584
+ parent: DetailParentSchema,
12585
+ steps: array(string()).optional()
12586
+ }), object({ details: array(DetailResultSchema) }).nullable(), { kind: "mutation" });
12454
12587
  /**
12455
12588
  * Hardware / firmware motion sensor cap — binary detected state plus
12456
12589
  * a timestamp of the last observation. Distinct from
@@ -15381,7 +15514,9 @@ var AddonPageDeclarationSchema$1 = object({
15381
15514
  icon: string(),
15382
15515
  path: string(),
15383
15516
  remoteName: string(),
15384
- bundle: string()
15517
+ bundle: string(),
15518
+ section: string().optional(),
15519
+ sectionLabel: string().optional()
15385
15520
  });
15386
15521
  var AddonPageInfoSchema = object({
15387
15522
  addonId: string(),
@@ -15421,7 +15556,18 @@ var AddonPageDeclarationSchema = object({
15421
15556
  * the static-file route can compute an mtime-based cache-buster URL
15422
15557
  * without a separate filesystem stat.
15423
15558
  */
15424
- bundle: string()
15559
+ bundle: string(),
15560
+ /**
15561
+ * Sidebar section this page docks into. Well-known ids: `'detection'`,
15562
+ * `'cluster'`, `'administration'` — the page renders inside that group.
15563
+ * Any OTHER string creates (or joins) a custom section rendered after
15564
+ * the built-in groups; its label comes from `sectionLabel` (first
15565
+ * declaration wins), falling back to the id. Absent → the legacy
15566
+ * "Addon Pages" group.
15567
+ */
15568
+ section: string().optional(),
15569
+ /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
15570
+ sectionLabel: string().optional()
15425
15571
  });
15426
15572
  method(_void(), array(AddonPageDeclarationSchema).readonly());
15427
15573
  var AddonHttpRouteSchema = object({
@@ -15656,6 +15802,17 @@ var WidgetMetadataSchema = object({
15656
15802
  deviceContext: boolean().default(false),
15657
15803
  integrationContext: boolean().default(false)
15658
15804
  }),
15805
+ /**
15806
+ * Loadable BEFORE authentication. The normal widget registry listing
15807
+ * (`addon-widgets.listWidgets`) is auth-gated, so a pre-auth surface
15808
+ * (the login page) cannot discover a widget through it. A widget that
15809
+ * declares `preAuth: true` marks itself as safe to mount on a pre-auth
15810
+ * screen — it is surfaced through the PUBLIC `auth.listLoginMethods`
15811
+ * login-method contribution channel (see `login-method.cap.ts`) rather
15812
+ * than the authenticated registry, and its bundle is served by the
15813
+ * public `/api/addon-widgets/:addonId/*` static route. Defaults false.
15814
+ */
15815
+ preAuth: boolean().optional().default(false),
15659
15816
  /** Dashboard placement HINTS (operator can override per instance). */
15660
15817
  defaultSize: WidgetSizeEnum.default("md"),
15661
15818
  allowedSizes: array(WidgetSizeEnum).readonly().default([
@@ -15957,6 +16114,66 @@ method(object({
15957
16114
  password: string()
15958
16115
  }), AuthResultSchema.nullable(), { kind: "mutation" }), method(object({ state: string() }), string()), method(record(string(), string()), AuthResultSchema, { kind: "mutation" }), method(object({ token: string() }), AuthResultSchema.nullable());
15959
16116
  /**
16117
+ * `login-method` — collection cap through which auth addons contribute
16118
+ * their pre-auth login surfaces to the login page. This is the SINGLE,
16119
+ * generic mechanism that supersedes the dead `auth.listProviders` reader:
16120
+ * every auth addon (OIDC, magic-link, WebAuthn/passkey) registers a
16121
+ * `login-method` provider and the PUBLIC `auth.listLoginMethods`
16122
+ * procedure aggregates them for the unauthenticated login page.
16123
+ *
16124
+ * A contribution is a discriminated union on `kind`:
16125
+ *
16126
+ * - `redirect` — a declarative button. The login page renders a generic
16127
+ * button that navigates to `startUrl` (an addon-owned HTTP route).
16128
+ * Covers OIDC (`/addon/auth-oidc/<id>/start`) and magic-link with
16129
+ * ZERO shell-side JS. A future SSO addon plugs in the same way — the
16130
+ * login page needs NO change.
16131
+ *
16132
+ * - `widget` — a Module-Federation widget the login page mounts (via
16133
+ * `loadRemoteBundle`) for an in-page ceremony. Covers the passkey
16134
+ * login ceremony, which must run `@simplewebauthn/browser` INSIDE the
16135
+ * addon bundle. The referenced widget also declares `preAuth: true` in
16136
+ * its `addon-widgets-source` catalog entry. `auth.listLoginMethods`
16137
+ * stamps a public `bundleUrl` from `addonId` + `bundle`.
16138
+ *
16139
+ * Every contribution carries a `stage`:
16140
+ * - `primary` — shown on the first credentials screen (OIDC /
16141
+ * magic-link buttons; a future usernameless passkey).
16142
+ * - `second-factor` — shown AFTER the password leg, gated on the
16143
+ * returned `factors` (passkey-as-2FA today).
16144
+ *
16145
+ * `mount: skip` — the cap is read server-side by the core auth router
16146
+ * (`registry.getCollection('login-method')`), never mounted as its own
16147
+ * tRPC router.
16148
+ */
16149
+ /** When a login method renders in the two-phase login flow. */
16150
+ var LoginStageEnum = _enum(["primary", "second-factor"]);
16151
+ /** One login-method contribution — redirect button OR pre-auth widget. */
16152
+ var LoginMethodContributionSchema = discriminatedUnion("kind", [object({
16153
+ kind: literal("redirect"),
16154
+ /** Stable id within the login-method set (e.g. `auth-oidc/google`). */
16155
+ id: string(),
16156
+ /** Operator-facing button label. */
16157
+ label: string(),
16158
+ /** lucide-react icon name. */
16159
+ icon: string().optional(),
16160
+ /** Addon-owned HTTP route the button navigates to (GET). */
16161
+ startUrl: string(),
16162
+ stage: LoginStageEnum
16163
+ }), object({
16164
+ kind: literal("widget"),
16165
+ /** Stable id within the login-method set (e.g. `auth-webauthn/passkey-login`). */
16166
+ id: string(),
16167
+ /** Owning addon id — drives the public bundle URL + the MF namespace. */
16168
+ addonId: string(),
16169
+ /** Bundle filename inside the addon's dist dir (`remoteEntry.js`). */
16170
+ bundle: string(),
16171
+ /** MF remote descriptor — `{ remoteName, exposedModule, componentKey }`. */
16172
+ remote: WidgetRemoteSchema,
16173
+ stage: LoginStageEnum
16174
+ })]);
16175
+ method(_void(), array(LoginMethodContributionSchema).readonly());
16176
+ /**
15960
16177
  * Orchestrator-side destination metadata. The orchestrator computes
15961
16178
  * `id = <addonId>:<subId>` from its provider lookup so consumers
15962
16179
  * (admin UI, restore flow) see one canonical key.
@@ -18126,7 +18343,17 @@ var TrackSchema = object({
18126
18343
  /** Cumulative normalized distance travelled (0..1 units = full frame width). */
18127
18344
  totalDistance: number(),
18128
18345
  state: TrackStateSchema,
18129
- active: boolean()
18346
+ active: boolean(),
18347
+ /** Deterministic key-event importance score in [0,1] (server-computed at
18348
+ * track expiry, recomputed on late label). Absent on legacy rows written
18349
+ * before scoring shipped — consumers degrade to absence / compute-on-read. */
18350
+ importance: number().optional(),
18351
+ /** Id of the track's highest-confidence ObjectEvent (its representative
18352
+ * "best" frame). Absent when the track produced no object events. */
18353
+ bestEventId: string().optional(),
18354
+ /** Tag of the importance sub-signal that dominated the score
18355
+ * (identity|dwell|proximity|class|confidence|travel|zone). */
18356
+ importanceReason: string().optional()
18130
18357
  });
18131
18358
  var BaseEventFields = {
18132
18359
  id: string(),
@@ -18191,8 +18418,18 @@ var ObjectEventSchema = object({
18191
18418
  frameHeight: number().optional(),
18192
18419
  /** MediaStore key for the crop attached to this event (if any). */
18193
18420
  mediaKey: string().optional(),
18421
+ /** Design B: MediaStore key of the track's native-resolution key frame (the
18422
+ * best-detection full frame). Resolve via the event-media data-plane
18423
+ * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
18424
+ * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
18425
+ * sources — consumers fall back to `mediaKey` (the tight crop). */
18426
+ keyFrameMediaKey: string().optional(),
18194
18427
  /** Populated by B5 (recording playback URL for this event). */
18195
- mediaUrl: string().optional()
18428
+ mediaUrl: string().optional(),
18429
+ /** The parent track's key-event importance [0,1], propagated to every object
18430
+ * event of the track (so an event row can be sorted by importance without a
18431
+ * track join). Absent on legacy rows / before the track was scored. */
18432
+ importance: number().optional()
18196
18433
  });
18197
18434
  var AudioEventSchema = object({
18198
18435
  ...BaseEventFields,
@@ -18216,7 +18453,8 @@ var MediaFileKindEnum = _enum([
18216
18453
  "fullFrame",
18217
18454
  "fullFrameBoxed",
18218
18455
  "faceCrop",
18219
- "plateCrop"
18456
+ "plateCrop",
18457
+ "keyFrame"
18220
18458
  ]);
18221
18459
  var MediaFileSchema = object({
18222
18460
  key: string(),
@@ -18237,6 +18475,32 @@ var DeviceEventQueryInput = object({
18237
18475
  projection: _enum(["full", "slim"]).optional()
18238
18476
  });
18239
18477
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
18478
+ var KeyEventQueryInput = object({
18479
+ deviceId: number(),
18480
+ /** Window lower bound (track firstSeen ≥ since). */
18481
+ since: number(),
18482
+ /** Window upper bound (track firstSeen ≤ until). */
18483
+ until: number(),
18484
+ limit: number().int().min(1).max(200).default(50),
18485
+ /** Drop tracks scoring below this importance. */
18486
+ minImportance: number().min(0).max(1).optional(),
18487
+ /** Restrict to a single class (e.g. 'person'). */
18488
+ classFilter: string().optional()
18489
+ });
18490
+ var KeyEventSchema = object({
18491
+ /** The representative event id (the track's best ObjectEvent, else its trackId). */
18492
+ id: string(),
18493
+ trackId: string(),
18494
+ /** Track start time (firstSeen). */
18495
+ timestamp: number(),
18496
+ className: string(),
18497
+ label: string().optional(),
18498
+ importance: number(),
18499
+ /** Highest-confidence ObjectEvent id for the track (empty when none). */
18500
+ bestEventId: string(),
18501
+ /** Track lifetime in ms (lastSeen - firstSeen). */
18502
+ windowMs: number().optional()
18503
+ });
18240
18504
  var TrackedDetectionSchema = object({
18241
18505
  trackId: string(),
18242
18506
  className: string(),
@@ -18266,7 +18530,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18266
18530
  }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
18267
18531
  kind: "mutation",
18268
18532
  auth: "admin"
18269
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({
18533
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
18270
18534
  deviceId: number(),
18271
18535
  since: number(),
18272
18536
  until: number(),
@@ -18311,11 +18575,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18311
18575
  timestamp: number()
18312
18576
  });
18313
18577
  var CameraPipelineConfigSchema = object({
18314
- engine: PipelineEngineChoiceSchema,
18578
+ engine: PipelineEngineChoiceSchema.optional(),
18315
18579
  steps: array(PipelineStepInputSchema).readonly(),
18316
18580
  audio: object({
18317
- engine: PipelineEngineChoiceSchema,
18318
- modelId: string(),
18581
+ engine: PipelineEngineChoiceSchema.optional(),
18582
+ modelId: string().optional(),
18319
18583
  enabled: boolean(),
18320
18584
  settings: record(string(), unknown()).readonly().optional()
18321
18585
  }).nullable().optional()
@@ -18330,7 +18594,7 @@ var PipelineTemplateSchema = object({
18330
18594
  });
18331
18595
  var AgentAddonConfigSchema = object({
18332
18596
  enabled: boolean(),
18333
- modelId: string(),
18597
+ modelId: string().optional(),
18334
18598
  settings: record(string(), unknown()).readonly()
18335
18599
  });
18336
18600
  var AgentPipelineSettingsSchema = object({
@@ -18340,12 +18604,25 @@ var AgentPipelineSettingsSchema = object({
18340
18604
  detectWeight: number().positive().optional(),
18341
18605
  /** Node is eligible to run the detection pipeline (decode + inference). */
18342
18606
  detect: boolean().optional(),
18343
- /** Node is eligible to host decoder sessions. */
18607
+ /**
18608
+ * DEPRECATED AND IGNORED. Decode is always co-located with its frame
18609
+ * consumer, so decode eligibility IS detect eligibility. Kept optional in
18610
+ * the schema ONLY so persisted stores written before the removal still
18611
+ * parse — no code reads it and no write path emits it.
18612
+ */
18344
18613
  decode: boolean().optional(),
18345
18614
  /** Node is eligible to run audio-analyzer sessions. */
18346
18615
  audio: boolean().optional(),
18347
18616
  /** Node is eligible to be the ingest / source-owner (serve the restream). */
18348
- ingest: boolean().optional()
18617
+ ingest: boolean().optional(),
18618
+ /**
18619
+ * Operator override for the LAN host a cross-node decoder dials to reach
18620
+ * THIS node's restream (Cluster UI). Absent → auto-detect: a remote runner
18621
+ * falls back to its `CAMSTACK_HUB_URL`-derived host (the Moleculer address
18622
+ * it already uses to reach the hub). Set this only when the auto-detected
18623
+ * address is wrong (multi-homed host, NAT, custom interface).
18624
+ */
18625
+ reachableHost: string().optional()
18349
18626
  });
18350
18627
  var CameraPipelineForAgentSchema = object({
18351
18628
  steps: array(PipelineStepInputSchema).readonly(),
@@ -18393,25 +18670,6 @@ var PipelineAssignmentSchema = object({
18393
18670
  assignedAt: number()
18394
18671
  });
18395
18672
  /**
18396
- * Decoder placement record. Symmetric to `PipelineAssignmentSchema` but for
18397
- * the decoder-node placement domain (`balanceDecoder` decision: manual pin
18398
- * → co-located with pipeline → capacity).
18399
- */
18400
- var DecoderAssignmentSchema = object({
18401
- deviceId: number(),
18402
- /** Moleculer node id of the decoder provider currently responsible for this camera. */
18403
- decoderNodeId: string(),
18404
- /** True when the assignment was set manually via `assignDecoder`, false when chosen by the balancer. */
18405
- pinned: boolean(),
18406
- /** Why this assignment was made — useful for debugging the decoder balancer. */
18407
- reason: _enum([
18408
- "manual",
18409
- "co-located",
18410
- "capacity",
18411
- "hardware-affinity"
18412
- ])
18413
- });
18414
- /**
18415
18673
  * Per-agent load summary surfaced to the load balancer + dashboards.
18416
18674
  * Aggregated from each runner's `getLocalLoad` cap call.
18417
18675
  */
@@ -18451,6 +18709,15 @@ var GlobalMetricsSchema = object({
18451
18709
  * capability providers.
18452
18710
  */
18453
18711
  var CapabilityBindingsSchema = record(string(), string());
18712
+ /**
18713
+ * The cluster's single camera-source owner (`clusterRoles.ingestNode`) plus
18714
+ * its LAN-reachable host, if one is registered. See `getIngestOwner`.
18715
+ */
18716
+ var IngestOwnerSchema = object({
18717
+ ownerNodeId: string(),
18718
+ reachableHost: string().optional(),
18719
+ configIssue: string().optional()
18720
+ });
18454
18721
  /** Source block — always present; derives from the stream catalog. */
18455
18722
  var CameraSourceStatusSchema = object({ streams: array(object({
18456
18723
  camStreamId: string(),
@@ -18465,6 +18732,14 @@ var CameraAssignmentStatusSchema = object({
18465
18732
  detectionNodeId: string().nullable(),
18466
18733
  decoderNodeId: string().nullable(),
18467
18734
  audioNodeId: string().nullable(),
18735
+ /**
18736
+ * The node that OWNS this camera's physical source pull (dials the RTSP and
18737
+ * hosts the broker/restream) — the cluster ingest owner today
18738
+ * (`clusterRoles.ingestNode`), per-camera once source assignment lands. Lets
18739
+ * the UI show WHERE a camera is sourced without SSH/logs, and is the node the
18740
+ * broker block below was read from (pinned). Nullable only pre-wiring.
18741
+ */
18742
+ sourceNodeId: string().nullable(),
18468
18743
  pinned: object({
18469
18744
  detection: boolean(),
18470
18745
  decoder: boolean(),
@@ -18597,16 +18872,7 @@ method(object({
18597
18872
  }), object({ success: literal(true) }), {
18598
18873
  kind: "mutation",
18599
18874
  auth: "admin"
18600
- }), method(object({
18601
- deviceId: number(),
18602
- nodeId: string()
18603
- }), _void(), {
18604
- kind: "mutation",
18605
- auth: "admin"
18606
- }), method(object({ deviceId: number() }), _void(), {
18607
- kind: "mutation",
18608
- auth: "admin"
18609
- }), method(_void(), array(DecoderAssignmentSchema).readonly()), method(object({
18875
+ }), method(_void(), IngestOwnerSchema), method(object({
18610
18876
  deviceId: number(),
18611
18877
  nodeId: string()
18612
18878
  }), object({ success: literal(true) }), {
@@ -18627,10 +18893,7 @@ method(object({
18627
18893
  nodeId: string(),
18628
18894
  pinned: boolean(),
18629
18895
  assignedAt: number()
18630
- }))), method(object({
18631
- deviceId: number(),
18632
- pipelineNodeId: string().optional()
18633
- }), DecoderAssignmentSchema), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
18896
+ }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
18634
18897
  nodeId: string(),
18635
18898
  settings: AgentPipelineSettingsSchema
18636
18899
  })).readonly()), method(object({
@@ -18660,12 +18923,26 @@ method(object({
18660
18923
  }), method(object({
18661
18924
  agentNodeId: string(),
18662
18925
  detect: boolean().nullable().optional(),
18663
- decode: boolean().nullable().optional(),
18664
18926
  audio: boolean().nullable().optional(),
18665
18927
  ingest: boolean().nullable().optional()
18666
18928
  }), object({ success: literal(true) }), {
18667
18929
  kind: "mutation",
18668
18930
  auth: "admin"
18931
+ }), method(object({
18932
+ agentNodeId: string(),
18933
+ reachableHost: string().nullable()
18934
+ }), object({ success: literal(true) }), {
18935
+ kind: "mutation",
18936
+ auth: "admin"
18937
+ }), method(object({ agentNodeId: string() }), object({
18938
+ success: literal(true),
18939
+ /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
18940
+ effectiveModelId: string().nullable(),
18941
+ /** Number of cameras whose node-scoped overrides were cleared. */
18942
+ clearedCameraOverrides: number()
18943
+ }), {
18944
+ kind: "mutation",
18945
+ auth: "admin"
18669
18946
  }), method(object({ deviceId: number() }), CameraPipelineSettingsSchema.nullable()), method(object({
18670
18947
  deviceId: number(),
18671
18948
  addonId: string(),
@@ -18710,22 +18987,131 @@ method(object({
18710
18987
  kind: "mutation",
18711
18988
  auth: "admin"
18712
18989
  });
18713
- var RegisteredStreamSchema = object({
18714
- streamId: string(),
18715
- label: string().optional(),
18716
- codec: string(),
18717
- type: _enum(["video", "audio"]),
18718
- sourceUrl: string()
18990
+ /**
18991
+ * server-management — per-NODE singleton capability for a node's ROOT
18992
+ * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
18993
+ * agents).
18994
+ *
18995
+ * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
18996
+ * on agents) carries the whole software stack in its npm dep tree, so ONE
18997
+ * version describes the node. Updates install into
18998
+ * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
18999
+ * starter (probation boot + auto-rollback to N-1).
19000
+ *
19001
+ * Providers:
19002
+ * - HUB: `ServerUpdateService` behind the `server-provided` mount
19003
+ * (`buildServerProviders` in trpc.router.ts) — the default target for
19004
+ * unpinned calls.
19005
+ * - AGENT: `AgentUpdateService` registered by the agent bootstrap under
19006
+ * the synthetic `agent-runtime` addonId and declared in the agent's
19007
+ * `$hub.registerNode` manifest.
19008
+ *
19009
+ * Node routing: singleton caps get the codegen/runtime-builder `nodeId`
19010
+ * injection on every method — `input.nodeId` (or `nodePin(nodeId)` from the
19011
+ * SDK) routes the call to that node's provider via the standard remote
19012
+ * proxy (`createCapabilityProxy` → `$agent-cap-fwd` → the agent's
19013
+ * in-process provider lookup). No `nodeId` → the hub's own provider.
19014
+ *
19015
+ * Spec: docs/superpowers/specs/2026-07-12-runtime-updatable-node-packages-design.md
19016
+ */
19017
+ /**
19018
+ * Where the running hub's code was loaded from:
19019
+ * - `workspace` — dev checkout (tsx / workspace dist); the starter defers to
19020
+ * plain resolution and runtime updates are refused.
19021
+ * - `baked` — the immutable image seed closure (no data-dir root active).
19022
+ * - `data-root` — the runtime-updatable `<dataDir>/server-root` closure.
19023
+ */
19024
+ var ServerBootModeSchema = _enum([
19025
+ "workspace",
19026
+ "baked",
19027
+ "data-root"
19028
+ ]);
19029
+ /**
19030
+ * Update lifecycle state:
19031
+ * - `idle` / `checking` / `staging` — steady / in-flight registry work.
19032
+ * - `pending-restart` — a version is staged and the node has NOT yet
19033
+ * restarted onto it (still running the OLD version).
19034
+ * - `awaiting-confirmation` — the node HAS restarted onto the staged version
19035
+ * (it is the active probation boot) and is waiting to confirm boot-health.
19036
+ * Apply/rollback are refused in this state and the node must NOT be
19037
+ * manually restarted, or the probation boot auto-rolls-back.
19038
+ */
19039
+ var ServerUpdateStateSchema = _enum([
19040
+ "idle",
19041
+ "checking",
19042
+ "staging",
19043
+ "pending-restart",
19044
+ "awaiting-confirmation"
19045
+ ]);
19046
+ var ServerRollbackInfoSchema = object({
19047
+ /** The version that failed (or was manually rolled back). */
19048
+ fromVersion: string(),
19049
+ /** The version rolled back to; null = the baked seed. */
19050
+ toVersion: string().nullable(),
19051
+ atMs: number(),
19052
+ reason: string()
18719
19053
  });
18720
- var ExposedResourceSchema = object({
18721
- streamId: string(),
18722
- format: string(),
18723
- value: string()
19054
+ var ServerPackageStatusSchema = object({
19055
+ /** Root package name (`@camstack/server` on the hub). */
19056
+ packageName: string(),
19057
+ /** Version of the code the running process ACTUALLY loaded. */
19058
+ runningVersion: string().nullable(),
19059
+ /** Node.js runtime version the node's process runs on (`process.versions.node`). */
19060
+ nodeRuntimeVersion: string().nullable(),
19061
+ /** Active data-dir root version; null when booted from seed/workspace. */
19062
+ activeVersion: string().nullable(),
19063
+ /** N-1 version kept for rollback; null when no previous version exists. */
19064
+ previousVersion: string().nullable(),
19065
+ /** Version of the immutable baked seed closure (image fallback). */
19066
+ seedVersion: string().nullable(),
19067
+ /** Latest registry version from the most recent check (null = never checked). */
19068
+ latestVersion: string().nullable(),
19069
+ updateAvailable: boolean(),
19070
+ bootMode: ServerBootModeSchema,
19071
+ updateState: ServerUpdateStateSchema,
19072
+ /** Version staged + awaiting its probation boot, when one is pending. */
19073
+ pendingVersion: string().nullable(),
19074
+ /** Set when the last freshly-activated version failed its boot health-check. */
19075
+ rolledBack: ServerRollbackInfoSchema.nullable(),
19076
+ /**
19077
+ * True when `server-root/state.json` EXISTS but is unreadable/corrupt — the
19078
+ * hub is running from the baked seed (or workspace) while installed data-dir
19079
+ * versions are being IGNORED. Surfaced as a warning in the UI.
19080
+ */
19081
+ stateFileCorrupt: boolean(),
19082
+ lastCheckedAtMs: number().nullable()
19083
+ });
19084
+ var ServerUpdateCheckResultSchema = object({
19085
+ packageName: string(),
19086
+ runningVersion: string().nullable(),
19087
+ latestVersion: string().nullable(),
19088
+ updateAvailable: boolean(),
19089
+ checkedAtMs: number(),
19090
+ /** Non-null when the registry lookup failed (offline, bad registry, …). */
19091
+ error: string().nullable()
19092
+ });
19093
+ var ServerUpdateActionResultSchema = object({
19094
+ accepted: boolean(),
19095
+ targetVersion: string().nullable(),
19096
+ /** True when a graceful restart was scheduled to apply the change. */
19097
+ restarting: boolean(),
19098
+ message: string()
19099
+ });
19100
+ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
19101
+ kind: "mutation",
19102
+ auth: "admin"
19103
+ }), method(object({
19104
+ /** Explicit target version; omitted = latest from the registry. */
19105
+ version: string().optional() }), ServerUpdateActionResultSchema, {
19106
+ kind: "mutation",
19107
+ auth: "admin"
19108
+ }), method(_void(), ServerUpdateActionResultSchema, {
19109
+ kind: "mutation",
19110
+ auth: "admin"
19111
+ }), method(_void(), ServerUpdateActionResultSchema, {
19112
+ kind: "mutation",
19113
+ auth: "admin"
18724
19114
  });
18725
- method(object({
18726
- deviceId: number(),
18727
- streams: array(RegisteredStreamSchema).readonly()
18728
- }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), array(ExposedResourceSchema).readonly());
18729
19115
  /**
18730
19116
  * Query filter for settings-store collections.
18731
19117
  */
@@ -18878,9 +19264,9 @@ method(SendEmailInputSchema, SendEmailResultSchema, {
18878
19264
  /**
18879
19265
  * A single device snapshot returned as base64 JPEG/PNG.
18880
19266
  *
18881
- * Shared with the `snapshot-provider` collection cap the orchestrator
18882
- * receives the same shape from each native provider and from the
18883
- * broker-based fallback.
19267
+ * The `SnapshotAddon` wrapper returns this shape whether the frame came from
19268
+ * the device-native provider (onboard capture) or from the stream-broker
19269
+ * prebuffer fallback.
18884
19270
  */
18885
19271
  var SnapshotImageSchema = object({
18886
19272
  base64: string(),
@@ -18911,11 +19297,12 @@ DeviceType.Camera, method(object({
18911
19297
  }), SnapshotImageSchema.nullable()), method(object({ deviceId: number() }), _void(), {
18912
19298
  kind: "mutation",
18913
19299
  auth: "admin"
18914
- });
18915
- method(object({ deviceId: number() }), boolean()), method(object({
19300
+ }), systemMethod(object({ deviceIds: array(number()).min(1).max(200) }), array(object({
18916
19301
  deviceId: number(),
18917
- streamId: string().optional()
18918
- }), SnapshotImageSchema.nullable());
19302
+ lastCapturedAt: number().nullable(),
19303
+ cacheAgeMs: number().nullable(),
19304
+ etag: string().nullable()
19305
+ })));
18919
19306
  /**
18920
19307
  * `sso-bridge` — internal hub-only cap that lets SSO-style auth
18921
19308
  * providers (OIDC, SAML, magic-link, …) mint an HMAC-signed token
@@ -19166,10 +19553,32 @@ method(_void(), array(TurnServerSchema).readonly());
19166
19553
  * b. `finishAuthentication({userId, response})` → server verifies
19167
19554
  * the assertion, bumps the credential counter, returns ok.
19168
19555
  *
19556
+ * 2b. Usernameless (discoverable-credential) authentication — the
19557
+ * passkey IS the primary factor, no password leg:
19558
+ * a. `beginDiscoverableAuthentication({})` → assertion options with
19559
+ * EMPTY `allowCredentials` (the browser offers every resident
19560
+ * passkey it holds for this RP) + `userVerification: 'required'`
19561
+ * (the passkey replaces both factors, so UV is mandatory).
19562
+ * The challenge is stored server-side, NOT bound to any user.
19563
+ * b. `finishDiscoverableAuthentication({response})` → the provider
19564
+ * resolves the credential by the response's credential id,
19565
+ * verifies the assertion against the stored challenge + that
19566
+ * credential's public key/counter, and returns the OWNING
19567
+ * `userId` — the caller (core auth router) mints the session.
19568
+ *
19169
19569
  * 3. Management:
19170
19570
  * - `listPasskeys({userId})` — enumerate user's enrolled credentials.
19171
19571
  * - `removePasskey({userId, credentialId})` — revoke one credential.
19172
19572
  *
19573
+ * 4. Second-factor preference (opt-in, default OFF):
19574
+ * Enrolling a passkey only enables passkey-FIRST sign-in. It is
19575
+ * demanded as a second factor after a password login ONLY when the
19576
+ * user explicitly opts in via `setSecondFactorPreference`.
19577
+ * - `getSecondFactorPreference({userId})` → `{ enabled }` (missing
19578
+ * row ⇒ `enabled: false`).
19579
+ * - `setSecondFactorPreference({userId, enabled})` — persisted by
19580
+ * the providing addon beside its credentials.
19581
+ *
19173
19582
  * Challenges are short-lived (5 min, in-memory). The cap is internal —
19174
19583
  * the admin-ui composes the begin/finish round-trip and never exposes
19175
19584
  * the cap to non-admins.
@@ -19212,6 +19621,17 @@ method(object({
19212
19621
  }), object({ verified: boolean() }), {
19213
19622
  kind: "mutation",
19214
19623
  access: "view"
19624
+ }), method(object({}), object({ optionsJSON: record(string(), unknown()) }), {
19625
+ kind: "mutation",
19626
+ access: "view"
19627
+ }), method(object({
19628
+ /** AuthenticationResponseJSON from the browser. */
19629
+ response: record(string(), unknown()) }), object({
19630
+ verified: boolean(),
19631
+ userId: string().nullable()
19632
+ }), {
19633
+ kind: "mutation",
19634
+ access: "view"
19215
19635
  }), method(object({ userId: string() }), array(PasskeySummarySchema), { auth: "admin" }), method(object({
19216
19636
  userId: string(),
19217
19637
  credentialId: string()
@@ -19219,6 +19639,13 @@ method(object({
19219
19639
  kind: "mutation",
19220
19640
  auth: "admin",
19221
19641
  access: "delete"
19642
+ }), method(object({ userId: string() }), object({ enabled: boolean() }), { auth: "admin" }), method(object({
19643
+ userId: string(),
19644
+ enabled: boolean()
19645
+ }), object({ success: literal(true) }), {
19646
+ kind: "mutation",
19647
+ auth: "admin",
19648
+ access: "create"
19222
19649
  });
19223
19650
  /**
19224
19651
  * `videoclips` — the unified, navigable-clip surface for a camera.
@@ -19276,9 +19703,10 @@ method(object({
19276
19703
  auth: "admin"
19277
19704
  });
19278
19705
  /**
19279
- * Optional client-side hints sent at session creation to help the
19280
- * provider pick the best native source. All fields are optional —
19281
- * a viewer that knows nothing still gets a sane default.
19706
+ * Optional client-side hints sent at session creation to help the provider
19707
+ * pick the best native source. All fields optional — a viewer that knows
19708
+ * nothing still gets a sane default. (Relocated from the retired `webrtc`
19709
+ * collection cap; this `webrtc-session` cap is the live signaling surface.)
19282
19710
  */
19283
19711
  var webrtcClientHintsSchema = object({
19284
19712
  viewportWidth: number().int().positive().optional(),
@@ -19289,22 +19717,6 @@ var webrtcClientHintsSchema = object({
19289
19717
  /** Hard tier override; takes precedence over scoring when registered. */
19290
19718
  prefersTier: string().optional()
19291
19719
  }).partial();
19292
- method(object({
19293
- streamId: string(),
19294
- sdpOffer: string()
19295
- }), string(), { kind: "mutation" }), method(object({ streamId: string() }), boolean()), method(object({
19296
- streamId: string(),
19297
- codec: string()
19298
- }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), _void(), { kind: "mutation" }), method(object({
19299
- streamId: string(),
19300
- hints: webrtcClientHintsSchema.optional()
19301
- }), object({
19302
- sessionId: string(),
19303
- sdpOffer: string()
19304
- }), { kind: "mutation" }), method(object({
19305
- sessionId: string(),
19306
- sdpAnswer: string()
19307
- }), _void(), { kind: "mutation" }), method(object({ sessionId: string() }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), boolean());
19308
19720
  /**
19309
19721
  * Discriminated target for a WebRTC session. The client sends this
19310
19722
  * structured object instead of building / parsing brokerId strings;
@@ -20052,7 +20464,17 @@ var FaceInfoSchema = object({
20052
20464
  recognizedIdentityId: string().optional(),
20053
20465
  identityName: string().optional(),
20054
20466
  assigned: boolean(),
20055
- base64: string().optional()
20467
+ base64: string().optional(),
20468
+ /** Design B: the face bbox (pixel space) on the key frame — lets a detail
20469
+ * view draw the box over the native `keyFrameMediaKey` frame. Absent on
20470
+ * legacy rows written before design B. */
20471
+ faceBbox: BoundingBoxSchema.optional(),
20472
+ /** Design B: MediaStore key of the track's native-resolution key frame.
20473
+ * Fetch the native JPEG via the event-media data-plane
20474
+ * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
20475
+ * track produced no key frame (e.g. native/onboard source) — the UI falls
20476
+ * back to the inline `base64` face crop. */
20477
+ keyFrameMediaKey: string().optional()
20056
20478
  });
20057
20479
  var FaceFilterEnum = _enum([
20058
20480
  "unassigned",
@@ -20749,6 +21171,16 @@ var TopologyCategorySchema = object({
20749
21171
  healthy: number(),
20750
21172
  addons: array(TopologyCategoryAddonSchema).readonly()
20751
21173
  });
21174
+ /**
21175
+ * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
21176
+ * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
21177
+ * version visibility for the Server management surface. Nullable: offline
21178
+ * rows and pre-phase-2 nodes report none.
21179
+ */
21180
+ var TopologyRootPackageSchema = object({
21181
+ name: string(),
21182
+ version: string()
21183
+ });
20752
21184
  var TopologyNodeSchema = object({
20753
21185
  id: string(),
20754
21186
  name: string(),
@@ -20772,7 +21204,8 @@ var TopologyNodeSchema = object({
20772
21204
  status: string()
20773
21205
  })).readonly(),
20774
21206
  processes: array(TopologyProcessSchema).readonly(),
20775
- categories: array(TopologyCategorySchema).readonly()
21207
+ categories: array(TopologyCategorySchema).readonly(),
21208
+ rootPackage: TopologyRootPackageSchema.nullable()
20776
21209
  });
20777
21210
  var CapUsageEdgeSchema = object({
20778
21211
  callerAddonId: string(),
@@ -23572,6 +24005,12 @@ Object.freeze({
23572
24005
  addonId: null,
23573
24006
  access: "create"
23574
24007
  },
24008
+ "loginMethod.getLoginMethods": {
24009
+ capName: "login-method",
24010
+ capScope: "system",
24011
+ addonId: null,
24012
+ access: "view"
24013
+ },
23575
24014
  "mediaPlayer.next": {
23576
24015
  capName: "media-player",
23577
24016
  capScope: "device",
@@ -24154,6 +24593,12 @@ Object.freeze({
24154
24593
  addonId: null,
24155
24594
  access: "view"
24156
24595
  },
24596
+ "pipelineAnalytics.getKeyEvents": {
24597
+ capName: "pipeline-analytics",
24598
+ capScope: "device",
24599
+ addonId: null,
24600
+ access: "view"
24601
+ },
24157
24602
  "pipelineAnalytics.getMotionEvents": {
24158
24603
  capName: "pipeline-analytics",
24159
24604
  capScope: "device",
@@ -24202,23 +24647,23 @@ Object.freeze({
24202
24647
  addonId: null,
24203
24648
  access: "create"
24204
24649
  },
24205
- "pipelineExecutor.deleteModel": {
24650
+ "pipelineExecutor.clearDeviceOverrides": {
24206
24651
  capName: "pipeline-executor",
24207
24652
  capScope: "system",
24208
24653
  addonId: null,
24209
24654
  access: "delete"
24210
24655
  },
24211
- "pipelineExecutor.deleteTemplate": {
24656
+ "pipelineExecutor.deleteModel": {
24212
24657
  capName: "pipeline-executor",
24213
24658
  capScope: "system",
24214
24659
  addonId: null,
24215
24660
  access: "delete"
24216
24661
  },
24217
- "pipelineExecutor.detect": {
24662
+ "pipelineExecutor.deleteTemplate": {
24218
24663
  capName: "pipeline-executor",
24219
24664
  capScope: "system",
24220
24665
  addonId: null,
24221
- access: "view"
24666
+ access: "delete"
24222
24667
  },
24223
24668
  "pipelineExecutor.downloadModel": {
24224
24669
  capName: "pipeline-executor",
@@ -24412,13 +24857,13 @@ Object.freeze({
24412
24857
  addonId: null,
24413
24858
  access: "create"
24414
24859
  },
24415
- "pipelineOrchestrator.assignAudio": {
24416
- capName: "pipeline-orchestrator",
24860
+ "pipelineExecutor.validatePipeline": {
24861
+ capName: "pipeline-executor",
24417
24862
  capScope: "system",
24418
24863
  addonId: null,
24419
- access: "create"
24864
+ access: "view"
24420
24865
  },
24421
- "pipelineOrchestrator.assignDecoder": {
24866
+ "pipelineOrchestrator.assignAudio": {
24422
24867
  capName: "pipeline-orchestrator",
24423
24868
  capScope: "system",
24424
24869
  addonId: null,
@@ -24502,19 +24947,13 @@ Object.freeze({
24502
24947
  addonId: null,
24503
24948
  access: "view"
24504
24949
  },
24505
- "pipelineOrchestrator.getDecoderAssignment": {
24506
- capName: "pipeline-orchestrator",
24507
- capScope: "system",
24508
- addonId: null,
24509
- access: "view"
24510
- },
24511
- "pipelineOrchestrator.getDecoderAssignments": {
24950
+ "pipelineOrchestrator.getGlobalMetrics": {
24512
24951
  capName: "pipeline-orchestrator",
24513
24952
  capScope: "system",
24514
24953
  addonId: null,
24515
24954
  access: "view"
24516
24955
  },
24517
- "pipelineOrchestrator.getGlobalMetrics": {
24956
+ "pipelineOrchestrator.getIngestOwner": {
24518
24957
  capName: "pipeline-orchestrator",
24519
24958
  capScope: "system",
24520
24959
  addonId: null,
@@ -24556,6 +24995,12 @@ Object.freeze({
24556
24995
  addonId: null,
24557
24996
  access: "delete"
24558
24997
  },
24998
+ "pipelineOrchestrator.resetNodePipelineDefaults": {
24999
+ capName: "pipeline-orchestrator",
25000
+ capScope: "system",
25001
+ addonId: null,
25002
+ access: "delete"
25003
+ },
24559
25004
  "pipelineOrchestrator.resolvePipeline": {
24560
25005
  capName: "pipeline-orchestrator",
24561
25006
  capScope: "system",
@@ -24592,37 +25037,37 @@ Object.freeze({
24592
25037
  addonId: null,
24593
25038
  access: "create"
24594
25039
  },
24595
- "pipelineOrchestrator.setCameraPipelineForAgent": {
25040
+ "pipelineOrchestrator.setAgentReachableHost": {
24596
25041
  capName: "pipeline-orchestrator",
24597
25042
  capScope: "system",
24598
25043
  addonId: null,
24599
25044
  access: "create"
24600
25045
  },
24601
- "pipelineOrchestrator.setCameraStepOverride": {
25046
+ "pipelineOrchestrator.setCameraPipelineForAgent": {
24602
25047
  capName: "pipeline-orchestrator",
24603
25048
  capScope: "system",
24604
25049
  addonId: null,
24605
25050
  access: "create"
24606
25051
  },
24607
- "pipelineOrchestrator.setCameraStepToggle": {
25052
+ "pipelineOrchestrator.setCameraStepOverride": {
24608
25053
  capName: "pipeline-orchestrator",
24609
25054
  capScope: "system",
24610
25055
  addonId: null,
24611
25056
  access: "create"
24612
25057
  },
24613
- "pipelineOrchestrator.setCapabilityBinding": {
25058
+ "pipelineOrchestrator.setCameraStepToggle": {
24614
25059
  capName: "pipeline-orchestrator",
24615
25060
  capScope: "system",
24616
25061
  addonId: null,
24617
25062
  access: "create"
24618
25063
  },
24619
- "pipelineOrchestrator.unassignAudio": {
25064
+ "pipelineOrchestrator.setCapabilityBinding": {
24620
25065
  capName: "pipeline-orchestrator",
24621
25066
  capScope: "system",
24622
25067
  addonId: null,
24623
25068
  access: "create"
24624
25069
  },
24625
- "pipelineOrchestrator.unassignDecoder": {
25070
+ "pipelineOrchestrator.unassignAudio": {
24626
25071
  capName: "pipeline-orchestrator",
24627
25072
  capScope: "system",
24628
25073
  addonId: null,
@@ -24682,12 +25127,24 @@ Object.freeze({
24682
25127
  addonId: null,
24683
25128
  access: "view"
24684
25129
  },
25130
+ "pipelineRunner.getNativeCrop": {
25131
+ capName: "pipeline-runner",
25132
+ capScope: "system",
25133
+ addonId: null,
25134
+ access: "view"
25135
+ },
24685
25136
  "pipelineRunner.reportMotion": {
24686
25137
  capName: "pipeline-runner",
24687
25138
  capScope: "system",
24688
25139
  addonId: null,
24689
25140
  access: "create"
24690
25141
  },
25142
+ "pipelineRunner.runDetailSubtree": {
25143
+ capName: "pipeline-runner",
25144
+ capScope: "system",
25145
+ addonId: null,
25146
+ access: "create"
25147
+ },
24691
25148
  "plateGallery.correctPlateText": {
24692
25149
  capName: "plate-gallery",
24693
25150
  capScope: "system",
@@ -24922,33 +25379,45 @@ Object.freeze({
24922
25379
  addonId: null,
24923
25380
  access: "create"
24924
25381
  },
24925
- "restreamer.getExposedResources": {
24926
- capName: "restreamer",
25382
+ "scriptRunner.run": {
25383
+ capName: "script-runner",
25384
+ capScope: "device",
25385
+ addonId: null,
25386
+ access: "create"
25387
+ },
25388
+ "scriptRunner.stop": {
25389
+ capName: "script-runner",
25390
+ capScope: "device",
25391
+ addonId: null,
25392
+ access: "create"
25393
+ },
25394
+ "serverManagement.applyServerUpdate": {
25395
+ capName: "server-management",
24927
25396
  capScope: "system",
24928
25397
  addonId: null,
24929
- access: "view"
25398
+ access: "create"
24930
25399
  },
24931
- "restreamer.registerDevice": {
24932
- capName: "restreamer",
25400
+ "serverManagement.checkServerUpdate": {
25401
+ capName: "server-management",
24933
25402
  capScope: "system",
24934
25403
  addonId: null,
24935
25404
  access: "create"
24936
25405
  },
24937
- "restreamer.unregisterDevice": {
24938
- capName: "restreamer",
25406
+ "serverManagement.getServerPackageStatus": {
25407
+ capName: "server-management",
24939
25408
  capScope: "system",
24940
25409
  addonId: null,
24941
- access: "delete"
25410
+ access: "view"
24942
25411
  },
24943
- "scriptRunner.run": {
24944
- capName: "script-runner",
24945
- capScope: "device",
25412
+ "serverManagement.restartServer": {
25413
+ capName: "server-management",
25414
+ capScope: "system",
24946
25415
  addonId: null,
24947
25416
  access: "create"
24948
25417
  },
24949
- "scriptRunner.stop": {
24950
- capName: "script-runner",
24951
- capScope: "device",
25418
+ "serverManagement.rollbackServerUpdate": {
25419
+ capName: "server-management",
25420
+ capScope: "system",
24952
25421
  addonId: null,
24953
25422
  access: "create"
24954
25423
  },
@@ -25036,23 +25505,17 @@ Object.freeze({
25036
25505
  addonId: null,
25037
25506
  access: "view"
25038
25507
  },
25039
- "snapshot.invalidateCache": {
25508
+ "snapshot.getSnapshotOverview": {
25040
25509
  capName: "snapshot",
25041
25510
  capScope: "device",
25042
25511
  addonId: null,
25043
- access: "create"
25044
- },
25045
- "snapshotProvider.getSnapshot": {
25046
- capName: "snapshot-provider",
25047
- capScope: "system",
25048
- addonId: null,
25049
25512
  access: "view"
25050
25513
  },
25051
- "snapshotProvider.supportsDevice": {
25052
- capName: "snapshot-provider",
25053
- capScope: "system",
25514
+ "snapshot.invalidateCache": {
25515
+ capName: "snapshot",
25516
+ capScope: "device",
25054
25517
  addonId: null,
25055
- access: "view"
25518
+ access: "create"
25056
25519
  },
25057
25520
  "ssoBridge.signBridgeToken": {
25058
25521
  capName: "sso-bridge",
@@ -25480,30 +25943,6 @@ Object.freeze({
25480
25943
  addonId: null,
25481
25944
  access: "view"
25482
25945
  },
25483
- "streamingEngine.getStreamUrl": {
25484
- capName: "streaming-engine",
25485
- capScope: "system",
25486
- addonId: null,
25487
- access: "view"
25488
- },
25489
- "streamingEngine.listStreams": {
25490
- capName: "streaming-engine",
25491
- capScope: "system",
25492
- addonId: null,
25493
- access: "view"
25494
- },
25495
- "streamingEngine.registerStream": {
25496
- capName: "streaming-engine",
25497
- capScope: "system",
25498
- addonId: null,
25499
- access: "create"
25500
- },
25501
- "streamingEngine.unregisterStream": {
25502
- capName: "streaming-engine",
25503
- capScope: "system",
25504
- addonId: null,
25505
- access: "delete"
25506
- },
25507
25946
  "streamParams.getConfigSchema": {
25508
25947
  capName: "stream-params",
25509
25948
  capScope: "device",
@@ -25750,6 +26189,12 @@ Object.freeze({
25750
26189
  addonId: null,
25751
26190
  access: "view"
25752
26191
  },
26192
+ "userPasskeys.beginDiscoverableAuthentication": {
26193
+ capName: "user-passkeys",
26194
+ capScope: "system",
26195
+ addonId: null,
26196
+ access: "view"
26197
+ },
25753
26198
  "userPasskeys.beginRegistration": {
25754
26199
  capName: "user-passkeys",
25755
26200
  capScope: "system",
@@ -25762,12 +26207,24 @@ Object.freeze({
25762
26207
  addonId: null,
25763
26208
  access: "view"
25764
26209
  },
26210
+ "userPasskeys.finishDiscoverableAuthentication": {
26211
+ capName: "user-passkeys",
26212
+ capScope: "system",
26213
+ addonId: null,
26214
+ access: "view"
26215
+ },
25765
26216
  "userPasskeys.finishRegistration": {
25766
26217
  capName: "user-passkeys",
25767
26218
  capScope: "system",
25768
26219
  addonId: null,
25769
26220
  access: "create"
25770
26221
  },
26222
+ "userPasskeys.getSecondFactorPreference": {
26223
+ capName: "user-passkeys",
26224
+ capScope: "system",
26225
+ addonId: null,
26226
+ access: "view"
26227
+ },
25771
26228
  "userPasskeys.listPasskeys": {
25772
26229
  capName: "user-passkeys",
25773
26230
  capScope: "system",
@@ -25780,6 +26237,12 @@ Object.freeze({
25780
26237
  addonId: null,
25781
26238
  access: "delete"
25782
26239
  },
26240
+ "userPasskeys.setSecondFactorPreference": {
26241
+ capName: "user-passkeys",
26242
+ capScope: "system",
26243
+ addonId: null,
26244
+ access: "create"
26245
+ },
25783
26246
  "vacuumControl.locate": {
25784
26247
  capName: "vacuum-control",
25785
26248
  capScope: "device",
@@ -25852,6 +26315,18 @@ Object.freeze({
25852
26315
  addonId: null,
25853
26316
  access: "view"
25854
26317
  },
26318
+ "viewerUi.getStaticDir": {
26319
+ capName: "viewer-ui",
26320
+ capScope: "system",
26321
+ addonId: null,
26322
+ access: "view"
26323
+ },
26324
+ "viewerUi.getVersion": {
26325
+ capName: "viewer-ui",
26326
+ capScope: "system",
26327
+ addonId: null,
26328
+ access: "view"
26329
+ },
25855
26330
  "waterHeater.setAway": {
25856
26331
  capName: "water-heater",
25857
26332
  capScope: "device",
@@ -25870,54 +26345,6 @@ Object.freeze({
25870
26345
  addonId: null,
25871
26346
  access: "create"
25872
26347
  },
25873
- "webrtc.closeSession": {
25874
- capName: "webrtc",
25875
- capScope: "system",
25876
- addonId: null,
25877
- access: "create"
25878
- },
25879
- "webrtc.createSession": {
25880
- capName: "webrtc",
25881
- capScope: "system",
25882
- addonId: null,
25883
- access: "create"
25884
- },
25885
- "webrtc.handleAnswer": {
25886
- capName: "webrtc",
25887
- capScope: "system",
25888
- addonId: null,
25889
- access: "create"
25890
- },
25891
- "webrtc.handleOffer": {
25892
- capName: "webrtc",
25893
- capScope: "system",
25894
- addonId: null,
25895
- access: "create"
25896
- },
25897
- "webrtc.hasAdaptiveBitrate": {
25898
- capName: "webrtc",
25899
- capScope: "system",
25900
- addonId: null,
25901
- access: "view"
25902
- },
25903
- "webrtc.registerStream": {
25904
- capName: "webrtc",
25905
- capScope: "system",
25906
- addonId: null,
25907
- access: "create"
25908
- },
25909
- "webrtc.supportsStream": {
25910
- capName: "webrtc",
25911
- capScope: "system",
25912
- addonId: null,
25913
- access: "view"
25914
- },
25915
- "webrtc.unregisterStream": {
25916
- capName: "webrtc",
25917
- capScope: "system",
25918
- addonId: null,
25919
- access: "delete"
25920
- },
25921
26348
  "webrtcSession.addIceCandidate": {
25922
26349
  capName: "webrtc-session",
25923
26350
  capScope: "device",