@camstack/addon-model-studio 1.0.18 → 1.0.20

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.
@@ -4638,7 +4638,7 @@ function _instanceof(cls, params = {}) {
4638
4638
  return inst;
4639
4639
  }
4640
4640
  //#endregion
4641
- //#region ../types/dist/sleep-CZDdRBua.mjs
4641
+ //#region ../types/dist/sleep-DiQ8xW1M.mjs
4642
4642
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4643
4643
  EventCategory["SystemBoot"] = "system.boot";
4644
4644
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -4824,6 +4824,18 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
4824
4824
  */
4825
4825
  EventCategory["PipelineCameraUpdated"] = "pipeline.camera-updated";
4826
4826
  /**
4827
+ * The cluster camera-source OWNER changed (`clusterRoles.ingestNode`).
4828
+ * Emitted by addon-pipeline-orchestrator whenever it (re)derives node
4829
+ * capabilities — at boot, on agent online/offline, and on an ingest-node
4830
+ * flip. Carries the resolved `ownerNodeId`. The stream-broker consumes it to
4831
+ * keep its ingest-owner-gate decision current WITHOUT a per-`ensureBroker`
4832
+ * cross-process `getIngestOwner` query (push the authority's decision instead
4833
+ * of polling it on the hot path). Idempotent state — re-emitted on every
4834
+ * topology change, so a dropped event self-heals on the next one (plus the
4835
+ * broker's long backstop reconcile query).
4836
+ */
4837
+ EventCategory["PipelineIngestOwnerChanged"] = "pipeline.ingest-owner-changed";
4838
+ /**
4827
4839
  * Periodic snapshot of per-node pipeline-runner load
4828
4840
  * (`RunnerLocalLoad`). Emitted ~1Hz by every runner so UI dashboards
4829
4841
  * subscribe instead of polling `pipelineRunner.getLocalLoad`.
@@ -6903,6 +6915,36 @@ var ModelFormatsSchema = object({
6903
6915
  tflite: ModelFormatEntrySchema.optional(),
6904
6916
  pt: ModelFormatEntrySchema.optional()
6905
6917
  });
6918
+ /**
6919
+ * Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
6920
+ * the reduced `PipelineModelOption` returned in `pipeline.getSchema()` so the
6921
+ * grouped Family→Tier→Variant picker renders identically in the config UI and
6922
+ * in the pipeline/device steppers. The flat `id` stays the source of truth for
6923
+ * resolution/download/persistence; this is a presentation overlay resolved back
6924
+ * to an `id`.
6925
+ */
6926
+ var ModelVariantGroupSchema = object({
6927
+ /** Top-level family, e.g. `yolo26` (later `d-fine`, `rf-detr`). */
6928
+ family: string(),
6929
+ /** Size within the family, e.g. `n` | `s` | `m` | `l`. */
6930
+ tier: string(),
6931
+ /** Quantization axis. Omit ⇒ the fp32 base build. */
6932
+ precision: _enum(["fp32", "int8"]).optional(),
6933
+ /**
6934
+ * Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
6935
+ * latency-optimized export (e.g. ReLU-activation variant) — the slot the
6936
+ * future performance variants plug into.
6937
+ */
6938
+ optimization: _enum(["standard", "fast"]).optional(),
6939
+ /**
6940
+ * Input-resolution axis (square input side, px). Omit ⇒ the family's native
6941
+ * resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
6942
+ * cheap latency lever — especially on Apple ANE and the Intel N100 — at a
6943
+ * small-object accuracy cost. Mirrors the model's `inputSize` but lifted onto
6944
+ * the group so the selector can offer it as a variant axis.
6945
+ */
6946
+ resolution: number().int().positive().optional()
6947
+ });
6906
6948
  var ModelCatalogEntrySchema = object({
6907
6949
  id: string(),
6908
6950
  name: string(),
@@ -6932,7 +6974,43 @@ var ModelCatalogEntrySchema = object({
6932
6974
  * Auxiliary files required at runtime (labels JSON, charset dict, etc.).
6933
6975
  * Downloaded into the same modelsDir alongside the model file.
6934
6976
  */
6935
- extraFiles: array(ModelExtraFileSchema).readonly().optional()
6977
+ extraFiles: array(ModelExtraFileSchema).readonly().optional(),
6978
+ /**
6979
+ * LEGACY entry — retained in the catalog so a persisted operator selection
6980
+ * still RESOLVES (and can be re-activated), but hidden from the selectable
6981
+ * model list and excluded from the auto format-default pick. Set on the
6982
+ * superseded / consolidated models (older lineages, redundant fp16 IRs) so
6983
+ * the active lineup stays the coherent curated ladder without deleting a
6984
+ * model anyone may still be pinned to. `resolveModelForFormat` keeps honoring
6985
+ * an explicit legacy id that has a build for the node's format.
6986
+ */
6987
+ legacy: boolean().optional(),
6988
+ /**
6989
+ * Measured quality/latency metadata — populated from the benchmark addon on
6990
+ * the real node classes. Absent = not yet measured (most entries today; the
6991
+ * catalog historically carried only `sizeMB`, a poor cross-architecture
6992
+ * speed proxy). `p95LatencyMs` is keyed by node class (e.g. `n100`, `mac`).
6993
+ */
6994
+ metrics: object({
6995
+ map50: number().optional(),
6996
+ p95LatencyMs: record(string(), number()).optional()
6997
+ }).optional(),
6998
+ /**
6999
+ * SPDX-ish license id of the model weights (e.g. `AGPL-3.0` for Ultralytics
7000
+ * YOLO26, `GPL-3.0` for YOLOv9, `Apache-2.0` for D-FINE/RF-DETR). Matters for
7001
+ * the retraining addon and any future commercial distribution.
7002
+ */
7003
+ license: string().optional(),
7004
+ /**
7005
+ * Variant-selector grouping. The UI groups models by `family` + `tier` and
7006
+ * offers `precision` / `optimization` as variant axes WITHIN a tier — so all
7007
+ * of a family's sizes and quantizations collapse into one grouped picker
7008
+ * instead of a flat list of `yolo26s`, `yolo26s-int8`, … Absent ⇒ ungrouped
7009
+ * (legacy / custom models) — never shown in the grouped selector. The flat
7010
+ * `id` stays the source of truth for resolution/download/persistence; grouping
7011
+ * is a presentation overlay resolved back to an `id`.
7012
+ */
7013
+ group: ModelVariantGroupSchema.optional()
6936
7014
  });
6937
7015
  var ConvertTargetSchema = discriminatedUnion("format", [object({
6938
7016
  format: literal("openvino"),
@@ -6994,8 +7072,8 @@ var RecordingModeSchema = _enum([
6994
7072
  "onAudioThreshold"
6995
7073
  ]);
6996
7074
  /**
6997
- * First-class, authoritative per-camera storage mode — the netta choice the UI
6998
- * reads directly (never inferred from `rules`):
7075
+ * First-class, authoritative per-camera storage mode — the explicit choice the
7076
+ * UI reads directly (never inferred from `rules`):
6999
7077
  * - `off` — not recording.
7000
7078
  * - `events` — record only around triggers (motion / audio threshold),
7001
7079
  * with pre/post-buffer.
@@ -8673,26 +8751,13 @@ DeviceType.Light, method(object({
8673
8751
  percentage: number().min(0).max(100),
8674
8752
  lastChangedAt: number()
8675
8753
  });
8754
+ /** Stream delivery format. (Relocated from the retired `streaming-engine` cap.) */
8676
8755
  var StreamFormatSchema = _enum([
8677
8756
  "webrtc",
8678
8757
  "hls",
8679
8758
  "mjpeg",
8680
8759
  "rtsp"
8681
8760
  ]);
8682
- var StreamInfoSchema = object({
8683
- streamId: string(),
8684
- format: StreamFormatSchema,
8685
- url: string().nullable(),
8686
- active: boolean()
8687
- });
8688
- method(object({
8689
- streamId: string(),
8690
- sourceUrl: string(),
8691
- codec: string().optional()
8692
- }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), _void(), { kind: "mutation" }), method(object({
8693
- streamId: string(),
8694
- format: StreamFormatSchema
8695
- }), string().nullable()), method(_void(), array(StreamInfoSchema));
8696
8761
  var RtspRestreamEntrySchema = object({
8697
8762
  brokerId: string(),
8698
8763
  url: string(),
@@ -9357,7 +9422,7 @@ var ConsumablesStatusSchema = object({
9357
9422
  })),
9358
9423
  lastChangedAt: number()
9359
9424
  });
9360
- DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, method(object({
9425
+ Object.values(DeviceType), method(object({
9361
9426
  deviceId: number().int().nonnegative(),
9362
9427
  key: string().min(1)
9363
9428
  }), _void(), {
@@ -10272,7 +10337,7 @@ var BoundingBoxSchema = object({
10272
10337
  w: number(),
10273
10338
  h: number()
10274
10339
  });
10275
- var SpatialDetectionSchema = object({
10340
+ object({
10276
10341
  class: string(),
10277
10342
  originalClass: string(),
10278
10343
  score: number(),
@@ -10407,7 +10472,6 @@ var PipelineDefaultStepSchema = lazy(() => object({
10407
10472
  enabled: boolean(),
10408
10473
  modelId: string(),
10409
10474
  children: array(PipelineDefaultStepSchema).readonly(),
10410
- engine: PipelineEngineChoiceSchema.optional(),
10411
10475
  group: string().optional(),
10412
10476
  settings: record(string(), unknown()).optional()
10413
10477
  }));
@@ -10432,7 +10496,9 @@ var PipelineModelOptionSchema = object({
10432
10496
  formats: record(string(), object({
10433
10497
  downloaded: boolean(),
10434
10498
  sizeMB: number()
10435
- }))
10499
+ })),
10500
+ group: ModelVariantGroupSchema.optional(),
10501
+ legacy: boolean().optional()
10436
10502
  });
10437
10503
  var ConfigFieldBridge = custom();
10438
10504
  var PipelineAddonSchemaSchema = object({
@@ -10462,11 +10528,6 @@ var PipelineSchemaSchema = object({
10462
10528
  selectedEngine: PipelineEngineChoiceSchema,
10463
10529
  slots: array(PipelineSlotSchemaSchema).readonly()
10464
10530
  });
10465
- var DetectorOutputSchema = object({
10466
- detections: array(SpatialDetectionSchema).readonly(),
10467
- inferenceMs: number(),
10468
- modelId: string()
10469
- });
10470
10531
  var EngineProvisioningSchema = object({
10471
10532
  runtimeId: _enum([
10472
10533
  "onnx",
@@ -10483,15 +10544,42 @@ var EngineProvisioningSchema = object({
10483
10544
  ]),
10484
10545
  progress: number().optional(),
10485
10546
  error: string().optional(),
10486
- nextRetryAt: number().optional()
10547
+ nextRetryAt: number().optional(),
10548
+ /**
10549
+ * Gate A (config-correctness gate at engine change): human-readable
10550
+ * config issues surfaced EAGERLY when the node's engine changes — model
10551
+ * substitutions ("chose X, running Y") and zero-build steps ("no model
10552
+ * has a <format> build"). Additive/optional: informational only, never
10553
+ * enforced here — `assertEngineReady` (readiness) still gates inference.
10554
+ * Absent/empty when the node-default tree resolves cleanly.
10555
+ */
10556
+ configIssues: array(string()).optional()
10487
10557
  });
10488
10558
  var PipelineStepInputSchema = lazy(() => object({
10489
10559
  addonId: string(),
10490
- modelId: string(),
10560
+ modelId: string().optional(),
10491
10561
  enabled: boolean().default(true),
10492
10562
  children: array(PipelineStepInputSchema).optional(),
10493
10563
  settings: record(string(), unknown()).optional()
10494
10564
  }));
10565
+ var ModelSubstitutionSchema = object({
10566
+ addonId: string(),
10567
+ chosen: string(),
10568
+ running: string(),
10569
+ format: string()
10570
+ });
10571
+ var PipelineValidationIssueSchema = object({
10572
+ addonId: string(),
10573
+ kind: _enum(["unknown-addon", "no-format-build"]),
10574
+ detail: string()
10575
+ });
10576
+ var PipelineValidationResultSchema = object({
10577
+ ok: boolean(),
10578
+ issues: array(PipelineValidationIssueSchema).readonly(),
10579
+ substitutions: array(ModelSubstitutionSchema).readonly(),
10580
+ /** The node's `currentEngine.format` this validation ran against. */
10581
+ format: string()
10582
+ });
10495
10583
  var ReferenceImageEntrySchema = object({
10496
10584
  filename: string(),
10497
10585
  stepIds: array(string()).readonly().optional()
@@ -10562,7 +10650,13 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
10562
10650
  })) }), object({ success: literal(true) }), {
10563
10651
  kind: "mutation",
10564
10652
  auth: "admin"
10565
- }), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
10653
+ }), method(object({ nodeId: string() }), object({
10654
+ success: literal(true),
10655
+ clearedDevices: number()
10656
+ }), {
10657
+ kind: "mutation",
10658
+ auth: "admin"
10659
+ }), 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({
10566
10660
  name: string(),
10567
10661
  steps: array(PipelineTemplateStepSchema).readonly(),
10568
10662
  engine: PipelineEngineChoiceSchema
@@ -10579,10 +10673,6 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
10579
10673
  modelId: string(),
10580
10674
  format: ModelFormatSchema$1
10581
10675
  }), object({ success: literal(true) }), { kind: "mutation" }), method(object({
10582
- addonId: string(),
10583
- frame: FrameInputSchema,
10584
- config: record(string(), unknown()).optional()
10585
- }), DetectorOutputSchema), method(object({
10586
10676
  engine: PipelineEngineChoiceSchema.optional(),
10587
10677
  steps: array(PipelineStepInputSchema).min(1),
10588
10678
  frame: FrameInputSchema.optional(),
@@ -10822,6 +10912,13 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
10822
10912
  kind: literal("remote-restream"),
10823
10913
  /** The camera's source-owner node (slice 1: always the hub). */
10824
10914
  ownerNodeId: string(),
10915
+ /**
10916
+ * The owner's LAN-reachable host, resolved by the orchestrator from the
10917
+ * per-node `reachableHost` override (Cluster UI). When present the runner
10918
+ * dials THIS host for the owner's restream, in preference to the
10919
+ * `CAMSTACK_HUB_URL`-derived default. Absent → auto-detect fallback.
10920
+ */
10921
+ ownerReachableHost: string().optional(),
10825
10922
  /** Operator override for the owner host the runner dials. */
10826
10923
  hubHostnameOverride: string().optional()
10827
10924
  })]).describe("Per-camera frame-source mode for the runner (P2c)");
@@ -10830,13 +10927,11 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
10830
10927
  * specific runner instance via `attachCamera`. Carries everything the
10831
10928
  * runner needs to subscribe to the local broker and execute inference.
10832
10929
  *
10833
- * Stateless-pipeline model: the full pipeline content (`engine`, `steps`,
10834
- * optional `audio`) travels with the attach payload. The runner keeps it
10835
- * in RAM for the lifetime of the attach — on rebalance, edit, or
10836
- * restart the orchestrator re-sends the latest snapshot.
10837
- *
10838
- * `engine`/`steps`/`audio` are optional during the additive migration
10839
- * window; once orchestrator + UI are migrated they become required.
10930
+ * Stateless-pipeline model: the pipeline content (`steps`, optional
10931
+ * `audio`) travels with the attach payload. The runner keeps it in RAM
10932
+ * for the lifetime of the attach — on rebalance, edit, or restart the
10933
+ * orchestrator re-sends the latest snapshot. Engine is NOT carried: it is
10934
+ * node-local, resolved by the executing runner at dispatch time.
10840
10935
  */
10841
10936
  var RunnerCameraConfigSchema = object({
10842
10937
  deviceId: number(),
@@ -10887,14 +10982,11 @@ var RunnerCameraConfigSchema = object({
10887
10982
  */
10888
10983
  motionSources: MotionSourcesSchema.default(["analyzer"]),
10889
10984
  pipelineEnabled: boolean().default(true),
10890
- /** Engine choice for video steps (runtime+backend+format). */
10891
- engine: PipelineEngineChoiceSchema.optional(),
10892
10985
  /** Ordered tree of video steps. Absent → runner skips video detection. */
10893
10986
  steps: array(PipelineStepInputSchema).readonly().optional(),
10894
10987
  /** Audio classification branch. `enabled:false` disables, null skips. */
10895
10988
  audio: object({
10896
- engine: PipelineEngineChoiceSchema,
10897
- modelId: string(),
10989
+ modelId: string().optional(),
10898
10990
  enabled: boolean()
10899
10991
  }).nullable().optional(),
10900
10992
  /**
@@ -12275,7 +12367,9 @@ var AddonPageDeclarationSchema$1 = object({
12275
12367
  icon: string(),
12276
12368
  path: string(),
12277
12369
  remoteName: string(),
12278
- bundle: string()
12370
+ bundle: string(),
12371
+ section: string().optional(),
12372
+ sectionLabel: string().optional()
12279
12373
  });
12280
12374
  var AddonPageInfoSchema = object({
12281
12375
  addonId: string(),
@@ -12315,7 +12409,18 @@ var AddonPageDeclarationSchema = object({
12315
12409
  * the static-file route can compute an mtime-based cache-buster URL
12316
12410
  * without a separate filesystem stat.
12317
12411
  */
12318
- bundle: string()
12412
+ bundle: string(),
12413
+ /**
12414
+ * Sidebar section this page docks into. Well-known ids: `'detection'`,
12415
+ * `'cluster'`, `'administration'` — the page renders inside that group.
12416
+ * Any OTHER string creates (or joins) a custom section rendered after
12417
+ * the built-in groups; its label comes from `sectionLabel` (first
12418
+ * declaration wins), falling back to the id. Absent → the legacy
12419
+ * "Addon Pages" group.
12420
+ */
12421
+ section: string().optional(),
12422
+ /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
12423
+ sectionLabel: string().optional()
12319
12424
  });
12320
12425
  var addonPagesSourceCapability = {
12321
12426
  name: "addon-pages-source",
@@ -15159,11 +15264,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
15159
15264
  timestamp: number()
15160
15265
  });
15161
15266
  var CameraPipelineConfigSchema = object({
15162
- engine: PipelineEngineChoiceSchema,
15267
+ engine: PipelineEngineChoiceSchema.optional(),
15163
15268
  steps: array(PipelineStepInputSchema).readonly(),
15164
15269
  audio: object({
15165
- engine: PipelineEngineChoiceSchema,
15166
- modelId: string(),
15270
+ engine: PipelineEngineChoiceSchema.optional(),
15271
+ modelId: string().optional(),
15167
15272
  enabled: boolean(),
15168
15273
  settings: record(string(), unknown()).readonly().optional()
15169
15274
  }).nullable().optional()
@@ -15178,7 +15283,7 @@ var PipelineTemplateSchema = object({
15178
15283
  });
15179
15284
  var AgentAddonConfigSchema = object({
15180
15285
  enabled: boolean(),
15181
- modelId: string(),
15286
+ modelId: string().optional(),
15182
15287
  settings: record(string(), unknown()).readonly()
15183
15288
  });
15184
15289
  var AgentPipelineSettingsSchema = object({
@@ -15188,12 +15293,25 @@ var AgentPipelineSettingsSchema = object({
15188
15293
  detectWeight: number().positive().optional(),
15189
15294
  /** Node is eligible to run the detection pipeline (decode + inference). */
15190
15295
  detect: boolean().optional(),
15191
- /** Node is eligible to host decoder sessions. */
15296
+ /**
15297
+ * DEPRECATED AND IGNORED. Decode is always co-located with its frame
15298
+ * consumer, so decode eligibility IS detect eligibility. Kept optional in
15299
+ * the schema ONLY so persisted stores written before the removal still
15300
+ * parse — no code reads it and no write path emits it.
15301
+ */
15192
15302
  decode: boolean().optional(),
15193
15303
  /** Node is eligible to run audio-analyzer sessions. */
15194
15304
  audio: boolean().optional(),
15195
15305
  /** Node is eligible to be the ingest / source-owner (serve the restream). */
15196
- ingest: boolean().optional()
15306
+ ingest: boolean().optional(),
15307
+ /**
15308
+ * Operator override for the LAN host a cross-node decoder dials to reach
15309
+ * THIS node's restream (Cluster UI). Absent → auto-detect: a remote runner
15310
+ * falls back to its `CAMSTACK_HUB_URL`-derived host (the Moleculer address
15311
+ * it already uses to reach the hub). Set this only when the auto-detected
15312
+ * address is wrong (multi-homed host, NAT, custom interface).
15313
+ */
15314
+ reachableHost: string().optional()
15197
15315
  });
15198
15316
  var CameraPipelineForAgentSchema = object({
15199
15317
  steps: array(PipelineStepInputSchema).readonly(),
@@ -15241,25 +15359,6 @@ var PipelineAssignmentSchema = object({
15241
15359
  assignedAt: number()
15242
15360
  });
15243
15361
  /**
15244
- * Decoder placement record. Symmetric to `PipelineAssignmentSchema` but for
15245
- * the decoder-node placement domain (`balanceDecoder` decision: manual pin
15246
- * → co-located with pipeline → capacity).
15247
- */
15248
- var DecoderAssignmentSchema = object({
15249
- deviceId: number(),
15250
- /** Moleculer node id of the decoder provider currently responsible for this camera. */
15251
- decoderNodeId: string(),
15252
- /** True when the assignment was set manually via `assignDecoder`, false when chosen by the balancer. */
15253
- pinned: boolean(),
15254
- /** Why this assignment was made — useful for debugging the decoder balancer. */
15255
- reason: _enum([
15256
- "manual",
15257
- "co-located",
15258
- "capacity",
15259
- "hardware-affinity"
15260
- ])
15261
- });
15262
- /**
15263
15362
  * Per-agent load summary surfaced to the load balancer + dashboards.
15264
15363
  * Aggregated from each runner's `getLocalLoad` cap call.
15265
15364
  */
@@ -15299,6 +15398,15 @@ var GlobalMetricsSchema = object({
15299
15398
  * capability providers.
15300
15399
  */
15301
15400
  var CapabilityBindingsSchema = record(string(), string());
15401
+ /**
15402
+ * The cluster's single camera-source owner (`clusterRoles.ingestNode`) plus
15403
+ * its LAN-reachable host, if one is registered. See `getIngestOwner`.
15404
+ */
15405
+ var IngestOwnerSchema = object({
15406
+ ownerNodeId: string(),
15407
+ reachableHost: string().optional(),
15408
+ configIssue: string().optional()
15409
+ });
15302
15410
  /** Source block — always present; derives from the stream catalog. */
15303
15411
  var CameraSourceStatusSchema = object({ streams: array(object({
15304
15412
  camStreamId: string(),
@@ -15313,6 +15421,14 @@ var CameraAssignmentStatusSchema = object({
15313
15421
  detectionNodeId: string().nullable(),
15314
15422
  decoderNodeId: string().nullable(),
15315
15423
  audioNodeId: string().nullable(),
15424
+ /**
15425
+ * The node that OWNS this camera's physical source pull (dials the RTSP and
15426
+ * hosts the broker/restream) — the cluster ingest owner today
15427
+ * (`clusterRoles.ingestNode`), per-camera once source assignment lands. Lets
15428
+ * the UI show WHERE a camera is sourced without SSH/logs, and is the node the
15429
+ * broker block below was read from (pinned). Nullable only pre-wiring.
15430
+ */
15431
+ sourceNodeId: string().nullable(),
15316
15432
  pinned: object({
15317
15433
  detection: boolean(),
15318
15434
  decoder: boolean(),
@@ -15445,16 +15561,7 @@ method(object({
15445
15561
  }), object({ success: literal(true) }), {
15446
15562
  kind: "mutation",
15447
15563
  auth: "admin"
15448
- }), method(object({
15449
- deviceId: number(),
15450
- nodeId: string()
15451
- }), _void(), {
15452
- kind: "mutation",
15453
- auth: "admin"
15454
- }), method(object({ deviceId: number() }), _void(), {
15455
- kind: "mutation",
15456
- auth: "admin"
15457
- }), method(_void(), array(DecoderAssignmentSchema).readonly()), method(object({
15564
+ }), method(_void(), IngestOwnerSchema), method(object({
15458
15565
  deviceId: number(),
15459
15566
  nodeId: string()
15460
15567
  }), object({ success: literal(true) }), {
@@ -15475,10 +15582,7 @@ method(object({
15475
15582
  nodeId: string(),
15476
15583
  pinned: boolean(),
15477
15584
  assignedAt: number()
15478
- }))), method(object({
15479
- deviceId: number(),
15480
- pipelineNodeId: string().optional()
15481
- }), DecoderAssignmentSchema), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
15585
+ }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
15482
15586
  nodeId: string(),
15483
15587
  settings: AgentPipelineSettingsSchema
15484
15588
  })).readonly()), method(object({
@@ -15508,12 +15612,26 @@ method(object({
15508
15612
  }), method(object({
15509
15613
  agentNodeId: string(),
15510
15614
  detect: boolean().nullable().optional(),
15511
- decode: boolean().nullable().optional(),
15512
15615
  audio: boolean().nullable().optional(),
15513
15616
  ingest: boolean().nullable().optional()
15514
15617
  }), object({ success: literal(true) }), {
15515
15618
  kind: "mutation",
15516
15619
  auth: "admin"
15620
+ }), method(object({
15621
+ agentNodeId: string(),
15622
+ reachableHost: string().nullable()
15623
+ }), object({ success: literal(true) }), {
15624
+ kind: "mutation",
15625
+ auth: "admin"
15626
+ }), method(object({ agentNodeId: string() }), object({
15627
+ success: literal(true),
15628
+ /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
15629
+ effectiveModelId: string().nullable(),
15630
+ /** Number of cameras whose node-scoped overrides were cleared. */
15631
+ clearedCameraOverrides: number()
15632
+ }), {
15633
+ kind: "mutation",
15634
+ auth: "admin"
15517
15635
  }), method(object({ deviceId: number() }), CameraPipelineSettingsSchema.nullable()), method(object({
15518
15636
  deviceId: number(),
15519
15637
  addonId: string(),
@@ -15558,22 +15676,6 @@ method(object({
15558
15676
  kind: "mutation",
15559
15677
  auth: "admin"
15560
15678
  });
15561
- var RegisteredStreamSchema = object({
15562
- streamId: string(),
15563
- label: string().optional(),
15564
- codec: string(),
15565
- type: _enum(["video", "audio"]),
15566
- sourceUrl: string()
15567
- });
15568
- var ExposedResourceSchema = object({
15569
- streamId: string(),
15570
- format: string(),
15571
- value: string()
15572
- });
15573
- method(object({
15574
- deviceId: number(),
15575
- streams: array(RegisteredStreamSchema).readonly()
15576
- }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), array(ExposedResourceSchema).readonly());
15577
15679
  /**
15578
15680
  * Query filter for settings-store collections.
15579
15681
  */
@@ -15726,9 +15828,9 @@ method(SendEmailInputSchema, SendEmailResultSchema, {
15726
15828
  /**
15727
15829
  * A single device snapshot returned as base64 JPEG/PNG.
15728
15830
  *
15729
- * Shared with the `snapshot-provider` collection cap the orchestrator
15730
- * receives the same shape from each native provider and from the
15731
- * broker-based fallback.
15831
+ * The `SnapshotAddon` wrapper returns this shape whether the frame came from
15832
+ * the device-native provider (onboard capture) or from the stream-broker
15833
+ * prebuffer fallback.
15732
15834
  */
15733
15835
  var SnapshotImageSchema = object({
15734
15836
  base64: string(),
@@ -15760,10 +15862,6 @@ DeviceType.Camera, method(object({
15760
15862
  kind: "mutation",
15761
15863
  auth: "admin"
15762
15864
  });
15763
- method(object({ deviceId: number() }), boolean()), method(object({
15764
- deviceId: number(),
15765
- streamId: string().optional()
15766
- }), SnapshotImageSchema.nullable());
15767
15865
  /**
15768
15866
  * `sso-bridge` — internal hub-only cap that lets SSO-style auth
15769
15867
  * providers (OIDC, SAML, magic-link, …) mint an HMAC-signed token
@@ -16124,9 +16222,10 @@ method(object({
16124
16222
  auth: "admin"
16125
16223
  });
16126
16224
  /**
16127
- * Optional client-side hints sent at session creation to help the
16128
- * provider pick the best native source. All fields are optional —
16129
- * a viewer that knows nothing still gets a sane default.
16225
+ * Optional client-side hints sent at session creation to help the provider
16226
+ * pick the best native source. All fields optional — a viewer that knows
16227
+ * nothing still gets a sane default. (Relocated from the retired `webrtc`
16228
+ * collection cap; this `webrtc-session` cap is the live signaling surface.)
16130
16229
  */
16131
16230
  var webrtcClientHintsSchema = object({
16132
16231
  viewportWidth: number().int().positive().optional(),
@@ -16137,22 +16236,6 @@ var webrtcClientHintsSchema = object({
16137
16236
  /** Hard tier override; takes precedence over scoring when registered. */
16138
16237
  prefersTier: string().optional()
16139
16238
  }).partial();
16140
- method(object({
16141
- streamId: string(),
16142
- sdpOffer: string()
16143
- }), string(), { kind: "mutation" }), method(object({ streamId: string() }), boolean()), method(object({
16144
- streamId: string(),
16145
- codec: string()
16146
- }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), _void(), { kind: "mutation" }), method(object({
16147
- streamId: string(),
16148
- hints: webrtcClientHintsSchema.optional()
16149
- }), object({
16150
- sessionId: string(),
16151
- sdpOffer: string()
16152
- }), { kind: "mutation" }), method(object({
16153
- sessionId: string(),
16154
- sdpAnswer: string()
16155
- }), _void(), { kind: "mutation" }), method(object({ sessionId: string() }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), boolean());
16156
16239
  /**
16157
16240
  * Discriminated target for a WebRTC session. The client sends this
16158
16241
  * structured object instead of building / parsing brokerId strings;
@@ -16639,7 +16722,15 @@ var FrameworkPackageStatusSchema = object({
16639
16722
  latestVersion: string().nullable(),
16640
16723
  hasUpdate: boolean(),
16641
16724
  /** Optional manifest description for the row tooltip. */
16642
- description: string().optional()
16725
+ description: string().optional(),
16726
+ /**
16727
+ * Content build-id (md5 of the resolved `dist/` tree) of the code the hub
16728
+ * ACTUALLY loaded. Framework packages ship code changes without always
16729
+ * bumping `currentVersion`, so semver alone hides "same version, new code".
16730
+ * `null` when the dist can't be hashed (not installed / empty). The admin-UI
16731
+ * surfaces this so a stale-code hub is visible even at an unchanged version.
16732
+ */
16733
+ buildId: string().nullable()
16643
16734
  });
16644
16735
  var LogStreamEntrySchema = object({
16645
16736
  timestamp: string(),
@@ -21025,23 +21116,23 @@ Object.freeze({
21025
21116
  addonId: null,
21026
21117
  access: "create"
21027
21118
  },
21028
- "pipelineExecutor.deleteModel": {
21119
+ "pipelineExecutor.clearDeviceOverrides": {
21029
21120
  capName: "pipeline-executor",
21030
21121
  capScope: "system",
21031
21122
  addonId: null,
21032
21123
  access: "delete"
21033
21124
  },
21034
- "pipelineExecutor.deleteTemplate": {
21125
+ "pipelineExecutor.deleteModel": {
21035
21126
  capName: "pipeline-executor",
21036
21127
  capScope: "system",
21037
21128
  addonId: null,
21038
21129
  access: "delete"
21039
21130
  },
21040
- "pipelineExecutor.detect": {
21131
+ "pipelineExecutor.deleteTemplate": {
21041
21132
  capName: "pipeline-executor",
21042
21133
  capScope: "system",
21043
21134
  addonId: null,
21044
- access: "view"
21135
+ access: "delete"
21045
21136
  },
21046
21137
  "pipelineExecutor.downloadModel": {
21047
21138
  capName: "pipeline-executor",
@@ -21235,13 +21326,13 @@ Object.freeze({
21235
21326
  addonId: null,
21236
21327
  access: "create"
21237
21328
  },
21238
- "pipelineOrchestrator.assignAudio": {
21239
- capName: "pipeline-orchestrator",
21329
+ "pipelineExecutor.validatePipeline": {
21330
+ capName: "pipeline-executor",
21240
21331
  capScope: "system",
21241
21332
  addonId: null,
21242
- access: "create"
21333
+ access: "view"
21243
21334
  },
21244
- "pipelineOrchestrator.assignDecoder": {
21335
+ "pipelineOrchestrator.assignAudio": {
21245
21336
  capName: "pipeline-orchestrator",
21246
21337
  capScope: "system",
21247
21338
  addonId: null,
@@ -21325,19 +21416,13 @@ Object.freeze({
21325
21416
  addonId: null,
21326
21417
  access: "view"
21327
21418
  },
21328
- "pipelineOrchestrator.getDecoderAssignment": {
21329
- capName: "pipeline-orchestrator",
21330
- capScope: "system",
21331
- addonId: null,
21332
- access: "view"
21333
- },
21334
- "pipelineOrchestrator.getDecoderAssignments": {
21419
+ "pipelineOrchestrator.getGlobalMetrics": {
21335
21420
  capName: "pipeline-orchestrator",
21336
21421
  capScope: "system",
21337
21422
  addonId: null,
21338
21423
  access: "view"
21339
21424
  },
21340
- "pipelineOrchestrator.getGlobalMetrics": {
21425
+ "pipelineOrchestrator.getIngestOwner": {
21341
21426
  capName: "pipeline-orchestrator",
21342
21427
  capScope: "system",
21343
21428
  addonId: null,
@@ -21379,6 +21464,12 @@ Object.freeze({
21379
21464
  addonId: null,
21380
21465
  access: "delete"
21381
21466
  },
21467
+ "pipelineOrchestrator.resetNodePipelineDefaults": {
21468
+ capName: "pipeline-orchestrator",
21469
+ capScope: "system",
21470
+ addonId: null,
21471
+ access: "delete"
21472
+ },
21382
21473
  "pipelineOrchestrator.resolvePipeline": {
21383
21474
  capName: "pipeline-orchestrator",
21384
21475
  capScope: "system",
@@ -21415,37 +21506,37 @@ Object.freeze({
21415
21506
  addonId: null,
21416
21507
  access: "create"
21417
21508
  },
21418
- "pipelineOrchestrator.setCameraPipelineForAgent": {
21509
+ "pipelineOrchestrator.setAgentReachableHost": {
21419
21510
  capName: "pipeline-orchestrator",
21420
21511
  capScope: "system",
21421
21512
  addonId: null,
21422
21513
  access: "create"
21423
21514
  },
21424
- "pipelineOrchestrator.setCameraStepOverride": {
21515
+ "pipelineOrchestrator.setCameraPipelineForAgent": {
21425
21516
  capName: "pipeline-orchestrator",
21426
21517
  capScope: "system",
21427
21518
  addonId: null,
21428
21519
  access: "create"
21429
21520
  },
21430
- "pipelineOrchestrator.setCameraStepToggle": {
21521
+ "pipelineOrchestrator.setCameraStepOverride": {
21431
21522
  capName: "pipeline-orchestrator",
21432
21523
  capScope: "system",
21433
21524
  addonId: null,
21434
21525
  access: "create"
21435
21526
  },
21436
- "pipelineOrchestrator.setCapabilityBinding": {
21527
+ "pipelineOrchestrator.setCameraStepToggle": {
21437
21528
  capName: "pipeline-orchestrator",
21438
21529
  capScope: "system",
21439
21530
  addonId: null,
21440
21531
  access: "create"
21441
21532
  },
21442
- "pipelineOrchestrator.unassignAudio": {
21533
+ "pipelineOrchestrator.setCapabilityBinding": {
21443
21534
  capName: "pipeline-orchestrator",
21444
21535
  capScope: "system",
21445
21536
  addonId: null,
21446
21537
  access: "create"
21447
21538
  },
21448
- "pipelineOrchestrator.unassignDecoder": {
21539
+ "pipelineOrchestrator.unassignAudio": {
21449
21540
  capName: "pipeline-orchestrator",
21450
21541
  capScope: "system",
21451
21542
  addonId: null,
@@ -21745,24 +21836,6 @@ Object.freeze({
21745
21836
  addonId: null,
21746
21837
  access: "create"
21747
21838
  },
21748
- "restreamer.getExposedResources": {
21749
- capName: "restreamer",
21750
- capScope: "system",
21751
- addonId: null,
21752
- access: "view"
21753
- },
21754
- "restreamer.registerDevice": {
21755
- capName: "restreamer",
21756
- capScope: "system",
21757
- addonId: null,
21758
- access: "create"
21759
- },
21760
- "restreamer.unregisterDevice": {
21761
- capName: "restreamer",
21762
- capScope: "system",
21763
- addonId: null,
21764
- access: "delete"
21765
- },
21766
21839
  "scriptRunner.run": {
21767
21840
  capName: "script-runner",
21768
21841
  capScope: "device",
@@ -21865,18 +21938,6 @@ Object.freeze({
21865
21938
  addonId: null,
21866
21939
  access: "create"
21867
21940
  },
21868
- "snapshotProvider.getSnapshot": {
21869
- capName: "snapshot-provider",
21870
- capScope: "system",
21871
- addonId: null,
21872
- access: "view"
21873
- },
21874
- "snapshotProvider.supportsDevice": {
21875
- capName: "snapshot-provider",
21876
- capScope: "system",
21877
- addonId: null,
21878
- access: "view"
21879
- },
21880
21941
  "ssoBridge.signBridgeToken": {
21881
21942
  capName: "sso-bridge",
21882
21943
  capScope: "system",
@@ -22303,30 +22364,6 @@ Object.freeze({
22303
22364
  addonId: null,
22304
22365
  access: "view"
22305
22366
  },
22306
- "streamingEngine.getStreamUrl": {
22307
- capName: "streaming-engine",
22308
- capScope: "system",
22309
- addonId: null,
22310
- access: "view"
22311
- },
22312
- "streamingEngine.listStreams": {
22313
- capName: "streaming-engine",
22314
- capScope: "system",
22315
- addonId: null,
22316
- access: "view"
22317
- },
22318
- "streamingEngine.registerStream": {
22319
- capName: "streaming-engine",
22320
- capScope: "system",
22321
- addonId: null,
22322
- access: "create"
22323
- },
22324
- "streamingEngine.unregisterStream": {
22325
- capName: "streaming-engine",
22326
- capScope: "system",
22327
- addonId: null,
22328
- access: "delete"
22329
- },
22330
22367
  "streamParams.getConfigSchema": {
22331
22368
  capName: "stream-params",
22332
22369
  capScope: "device",
@@ -22693,54 +22730,6 @@ Object.freeze({
22693
22730
  addonId: null,
22694
22731
  access: "create"
22695
22732
  },
22696
- "webrtc.closeSession": {
22697
- capName: "webrtc",
22698
- capScope: "system",
22699
- addonId: null,
22700
- access: "create"
22701
- },
22702
- "webrtc.createSession": {
22703
- capName: "webrtc",
22704
- capScope: "system",
22705
- addonId: null,
22706
- access: "create"
22707
- },
22708
- "webrtc.handleAnswer": {
22709
- capName: "webrtc",
22710
- capScope: "system",
22711
- addonId: null,
22712
- access: "create"
22713
- },
22714
- "webrtc.handleOffer": {
22715
- capName: "webrtc",
22716
- capScope: "system",
22717
- addonId: null,
22718
- access: "create"
22719
- },
22720
- "webrtc.hasAdaptiveBitrate": {
22721
- capName: "webrtc",
22722
- capScope: "system",
22723
- addonId: null,
22724
- access: "view"
22725
- },
22726
- "webrtc.registerStream": {
22727
- capName: "webrtc",
22728
- capScope: "system",
22729
- addonId: null,
22730
- access: "create"
22731
- },
22732
- "webrtc.supportsStream": {
22733
- capName: "webrtc",
22734
- capScope: "system",
22735
- addonId: null,
22736
- access: "view"
22737
- },
22738
- "webrtc.unregisterStream": {
22739
- capName: "webrtc",
22740
- capScope: "system",
22741
- addonId: null,
22742
- access: "delete"
22743
- },
22744
22733
  "webrtcSession.addIceCandidate": {
22745
22734
  capName: "webrtc-session",
22746
22735
  capScope: "device",
@@ -23254,7 +23243,8 @@ var ModelStudioAddon = class extends BaseAddon {
23254
23243
  icon: "boxes",
23255
23244
  path: "/addon/model-studio",
23256
23245
  remoteName: "addon_model_studio_page",
23257
- bundle: "remoteEntry.js"
23246
+ bundle: "remoteEntry.js",
23247
+ section: "detection"
23258
23248
  }];
23259
23249
  constructor() {
23260
23250
  super({});