@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.
@@ -4661,7 +4661,7 @@ function _instanceof(cls, params = {}) {
4661
4661
  return inst;
4662
4662
  }
4663
4663
  //#endregion
4664
- //#region ../types/dist/sleep-CZDdRBua.mjs
4664
+ //#region ../types/dist/sleep-DiQ8xW1M.mjs
4665
4665
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4666
4666
  EventCategory["SystemBoot"] = "system.boot";
4667
4667
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -4847,6 +4847,18 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
4847
4847
  */
4848
4848
  EventCategory["PipelineCameraUpdated"] = "pipeline.camera-updated";
4849
4849
  /**
4850
+ * The cluster camera-source OWNER changed (`clusterRoles.ingestNode`).
4851
+ * Emitted by addon-pipeline-orchestrator whenever it (re)derives node
4852
+ * capabilities — at boot, on agent online/offline, and on an ingest-node
4853
+ * flip. Carries the resolved `ownerNodeId`. The stream-broker consumes it to
4854
+ * keep its ingest-owner-gate decision current WITHOUT a per-`ensureBroker`
4855
+ * cross-process `getIngestOwner` query (push the authority's decision instead
4856
+ * of polling it on the hot path). Idempotent state — re-emitted on every
4857
+ * topology change, so a dropped event self-heals on the next one (plus the
4858
+ * broker's long backstop reconcile query).
4859
+ */
4860
+ EventCategory["PipelineIngestOwnerChanged"] = "pipeline.ingest-owner-changed";
4861
+ /**
4850
4862
  * Periodic snapshot of per-node pipeline-runner load
4851
4863
  * (`RunnerLocalLoad`). Emitted ~1Hz by every runner so UI dashboards
4852
4864
  * subscribe instead of polling `pipelineRunner.getLocalLoad`.
@@ -6926,6 +6938,36 @@ var ModelFormatsSchema = object({
6926
6938
  tflite: ModelFormatEntrySchema.optional(),
6927
6939
  pt: ModelFormatEntrySchema.optional()
6928
6940
  });
6941
+ /**
6942
+ * Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
6943
+ * the reduced `PipelineModelOption` returned in `pipeline.getSchema()` so the
6944
+ * grouped Family→Tier→Variant picker renders identically in the config UI and
6945
+ * in the pipeline/device steppers. The flat `id` stays the source of truth for
6946
+ * resolution/download/persistence; this is a presentation overlay resolved back
6947
+ * to an `id`.
6948
+ */
6949
+ var ModelVariantGroupSchema = object({
6950
+ /** Top-level family, e.g. `yolo26` (later `d-fine`, `rf-detr`). */
6951
+ family: string(),
6952
+ /** Size within the family, e.g. `n` | `s` | `m` | `l`. */
6953
+ tier: string(),
6954
+ /** Quantization axis. Omit ⇒ the fp32 base build. */
6955
+ precision: _enum(["fp32", "int8"]).optional(),
6956
+ /**
6957
+ * Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
6958
+ * latency-optimized export (e.g. ReLU-activation variant) — the slot the
6959
+ * future performance variants plug into.
6960
+ */
6961
+ optimization: _enum(["standard", "fast"]).optional(),
6962
+ /**
6963
+ * Input-resolution axis (square input side, px). Omit ⇒ the family's native
6964
+ * resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
6965
+ * cheap latency lever — especially on Apple ANE and the Intel N100 — at a
6966
+ * small-object accuracy cost. Mirrors the model's `inputSize` but lifted onto
6967
+ * the group so the selector can offer it as a variant axis.
6968
+ */
6969
+ resolution: number().int().positive().optional()
6970
+ });
6929
6971
  var ModelCatalogEntrySchema = object({
6930
6972
  id: string(),
6931
6973
  name: string(),
@@ -6955,7 +6997,43 @@ var ModelCatalogEntrySchema = object({
6955
6997
  * Auxiliary files required at runtime (labels JSON, charset dict, etc.).
6956
6998
  * Downloaded into the same modelsDir alongside the model file.
6957
6999
  */
6958
- extraFiles: array(ModelExtraFileSchema).readonly().optional()
7000
+ extraFiles: array(ModelExtraFileSchema).readonly().optional(),
7001
+ /**
7002
+ * LEGACY entry — retained in the catalog so a persisted operator selection
7003
+ * still RESOLVES (and can be re-activated), but hidden from the selectable
7004
+ * model list and excluded from the auto format-default pick. Set on the
7005
+ * superseded / consolidated models (older lineages, redundant fp16 IRs) so
7006
+ * the active lineup stays the coherent curated ladder without deleting a
7007
+ * model anyone may still be pinned to. `resolveModelForFormat` keeps honoring
7008
+ * an explicit legacy id that has a build for the node's format.
7009
+ */
7010
+ legacy: boolean().optional(),
7011
+ /**
7012
+ * Measured quality/latency metadata — populated from the benchmark addon on
7013
+ * the real node classes. Absent = not yet measured (most entries today; the
7014
+ * catalog historically carried only `sizeMB`, a poor cross-architecture
7015
+ * speed proxy). `p95LatencyMs` is keyed by node class (e.g. `n100`, `mac`).
7016
+ */
7017
+ metrics: object({
7018
+ map50: number().optional(),
7019
+ p95LatencyMs: record(string(), number()).optional()
7020
+ }).optional(),
7021
+ /**
7022
+ * SPDX-ish license id of the model weights (e.g. `AGPL-3.0` for Ultralytics
7023
+ * YOLO26, `GPL-3.0` for YOLOv9, `Apache-2.0` for D-FINE/RF-DETR). Matters for
7024
+ * the retraining addon and any future commercial distribution.
7025
+ */
7026
+ license: string().optional(),
7027
+ /**
7028
+ * Variant-selector grouping. The UI groups models by `family` + `tier` and
7029
+ * offers `precision` / `optimization` as variant axes WITHIN a tier — so all
7030
+ * of a family's sizes and quantizations collapse into one grouped picker
7031
+ * instead of a flat list of `yolo26s`, `yolo26s-int8`, … Absent ⇒ ungrouped
7032
+ * (legacy / custom models) — never shown in the grouped selector. The flat
7033
+ * `id` stays the source of truth for resolution/download/persistence; grouping
7034
+ * is a presentation overlay resolved back to an `id`.
7035
+ */
7036
+ group: ModelVariantGroupSchema.optional()
6959
7037
  });
6960
7038
  var ConvertTargetSchema = discriminatedUnion("format", [object({
6961
7039
  format: literal("openvino"),
@@ -7017,8 +7095,8 @@ var RecordingModeSchema = _enum([
7017
7095
  "onAudioThreshold"
7018
7096
  ]);
7019
7097
  /**
7020
- * First-class, authoritative per-camera storage mode — the netta choice the UI
7021
- * reads directly (never inferred from `rules`):
7098
+ * First-class, authoritative per-camera storage mode — the explicit choice the
7099
+ * UI reads directly (never inferred from `rules`):
7022
7100
  * - `off` — not recording.
7023
7101
  * - `events` — record only around triggers (motion / audio threshold),
7024
7102
  * with pre/post-buffer.
@@ -8696,26 +8774,13 @@ DeviceType.Light, method(object({
8696
8774
  percentage: number().min(0).max(100),
8697
8775
  lastChangedAt: number()
8698
8776
  });
8777
+ /** Stream delivery format. (Relocated from the retired `streaming-engine` cap.) */
8699
8778
  var StreamFormatSchema = _enum([
8700
8779
  "webrtc",
8701
8780
  "hls",
8702
8781
  "mjpeg",
8703
8782
  "rtsp"
8704
8783
  ]);
8705
- var StreamInfoSchema = object({
8706
- streamId: string(),
8707
- format: StreamFormatSchema,
8708
- url: string().nullable(),
8709
- active: boolean()
8710
- });
8711
- method(object({
8712
- streamId: string(),
8713
- sourceUrl: string(),
8714
- codec: string().optional()
8715
- }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), _void(), { kind: "mutation" }), method(object({
8716
- streamId: string(),
8717
- format: StreamFormatSchema
8718
- }), string().nullable()), method(_void(), array(StreamInfoSchema));
8719
8784
  var RtspRestreamEntrySchema = object({
8720
8785
  brokerId: string(),
8721
8786
  url: string(),
@@ -9380,7 +9445,7 @@ var ConsumablesStatusSchema = object({
9380
9445
  })),
9381
9446
  lastChangedAt: number()
9382
9447
  });
9383
- 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({
9448
+ Object.values(DeviceType), method(object({
9384
9449
  deviceId: number().int().nonnegative(),
9385
9450
  key: string().min(1)
9386
9451
  }), _void(), {
@@ -10295,7 +10360,7 @@ var BoundingBoxSchema = object({
10295
10360
  w: number(),
10296
10361
  h: number()
10297
10362
  });
10298
- var SpatialDetectionSchema = object({
10363
+ object({
10299
10364
  class: string(),
10300
10365
  originalClass: string(),
10301
10366
  score: number(),
@@ -10430,7 +10495,6 @@ var PipelineDefaultStepSchema = lazy(() => object({
10430
10495
  enabled: boolean(),
10431
10496
  modelId: string(),
10432
10497
  children: array(PipelineDefaultStepSchema).readonly(),
10433
- engine: PipelineEngineChoiceSchema.optional(),
10434
10498
  group: string().optional(),
10435
10499
  settings: record(string(), unknown()).optional()
10436
10500
  }));
@@ -10455,7 +10519,9 @@ var PipelineModelOptionSchema = object({
10455
10519
  formats: record(string(), object({
10456
10520
  downloaded: boolean(),
10457
10521
  sizeMB: number()
10458
- }))
10522
+ })),
10523
+ group: ModelVariantGroupSchema.optional(),
10524
+ legacy: boolean().optional()
10459
10525
  });
10460
10526
  var ConfigFieldBridge = custom();
10461
10527
  var PipelineAddonSchemaSchema = object({
@@ -10485,11 +10551,6 @@ var PipelineSchemaSchema = object({
10485
10551
  selectedEngine: PipelineEngineChoiceSchema,
10486
10552
  slots: array(PipelineSlotSchemaSchema).readonly()
10487
10553
  });
10488
- var DetectorOutputSchema = object({
10489
- detections: array(SpatialDetectionSchema).readonly(),
10490
- inferenceMs: number(),
10491
- modelId: string()
10492
- });
10493
10554
  var EngineProvisioningSchema = object({
10494
10555
  runtimeId: _enum([
10495
10556
  "onnx",
@@ -10506,15 +10567,42 @@ var EngineProvisioningSchema = object({
10506
10567
  ]),
10507
10568
  progress: number().optional(),
10508
10569
  error: string().optional(),
10509
- nextRetryAt: number().optional()
10570
+ nextRetryAt: number().optional(),
10571
+ /**
10572
+ * Gate A (config-correctness gate at engine change): human-readable
10573
+ * config issues surfaced EAGERLY when the node's engine changes — model
10574
+ * substitutions ("chose X, running Y") and zero-build steps ("no model
10575
+ * has a <format> build"). Additive/optional: informational only, never
10576
+ * enforced here — `assertEngineReady` (readiness) still gates inference.
10577
+ * Absent/empty when the node-default tree resolves cleanly.
10578
+ */
10579
+ configIssues: array(string()).optional()
10510
10580
  });
10511
10581
  var PipelineStepInputSchema = lazy(() => object({
10512
10582
  addonId: string(),
10513
- modelId: string(),
10583
+ modelId: string().optional(),
10514
10584
  enabled: boolean().default(true),
10515
10585
  children: array(PipelineStepInputSchema).optional(),
10516
10586
  settings: record(string(), unknown()).optional()
10517
10587
  }));
10588
+ var ModelSubstitutionSchema = object({
10589
+ addonId: string(),
10590
+ chosen: string(),
10591
+ running: string(),
10592
+ format: string()
10593
+ });
10594
+ var PipelineValidationIssueSchema = object({
10595
+ addonId: string(),
10596
+ kind: _enum(["unknown-addon", "no-format-build"]),
10597
+ detail: string()
10598
+ });
10599
+ var PipelineValidationResultSchema = object({
10600
+ ok: boolean(),
10601
+ issues: array(PipelineValidationIssueSchema).readonly(),
10602
+ substitutions: array(ModelSubstitutionSchema).readonly(),
10603
+ /** The node's `currentEngine.format` this validation ran against. */
10604
+ format: string()
10605
+ });
10518
10606
  var ReferenceImageEntrySchema = object({
10519
10607
  filename: string(),
10520
10608
  stepIds: array(string()).readonly().optional()
@@ -10585,7 +10673,13 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
10585
10673
  })) }), object({ success: literal(true) }), {
10586
10674
  kind: "mutation",
10587
10675
  auth: "admin"
10588
- }), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
10676
+ }), method(object({ nodeId: string() }), object({
10677
+ success: literal(true),
10678
+ clearedDevices: number()
10679
+ }), {
10680
+ kind: "mutation",
10681
+ auth: "admin"
10682
+ }), 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({
10589
10683
  name: string(),
10590
10684
  steps: array(PipelineTemplateStepSchema).readonly(),
10591
10685
  engine: PipelineEngineChoiceSchema
@@ -10602,10 +10696,6 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
10602
10696
  modelId: string(),
10603
10697
  format: ModelFormatSchema$1
10604
10698
  }), object({ success: literal(true) }), { kind: "mutation" }), method(object({
10605
- addonId: string(),
10606
- frame: FrameInputSchema,
10607
- config: record(string(), unknown()).optional()
10608
- }), DetectorOutputSchema), method(object({
10609
10699
  engine: PipelineEngineChoiceSchema.optional(),
10610
10700
  steps: array(PipelineStepInputSchema).min(1),
10611
10701
  frame: FrameInputSchema.optional(),
@@ -10845,6 +10935,13 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
10845
10935
  kind: literal("remote-restream"),
10846
10936
  /** The camera's source-owner node (slice 1: always the hub). */
10847
10937
  ownerNodeId: string(),
10938
+ /**
10939
+ * The owner's LAN-reachable host, resolved by the orchestrator from the
10940
+ * per-node `reachableHost` override (Cluster UI). When present the runner
10941
+ * dials THIS host for the owner's restream, in preference to the
10942
+ * `CAMSTACK_HUB_URL`-derived default. Absent → auto-detect fallback.
10943
+ */
10944
+ ownerReachableHost: string().optional(),
10848
10945
  /** Operator override for the owner host the runner dials. */
10849
10946
  hubHostnameOverride: string().optional()
10850
10947
  })]).describe("Per-camera frame-source mode for the runner (P2c)");
@@ -10853,13 +10950,11 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
10853
10950
  * specific runner instance via `attachCamera`. Carries everything the
10854
10951
  * runner needs to subscribe to the local broker and execute inference.
10855
10952
  *
10856
- * Stateless-pipeline model: the full pipeline content (`engine`, `steps`,
10857
- * optional `audio`) travels with the attach payload. The runner keeps it
10858
- * in RAM for the lifetime of the attach — on rebalance, edit, or
10859
- * restart the orchestrator re-sends the latest snapshot.
10860
- *
10861
- * `engine`/`steps`/`audio` are optional during the additive migration
10862
- * window; once orchestrator + UI are migrated they become required.
10953
+ * Stateless-pipeline model: the pipeline content (`steps`, optional
10954
+ * `audio`) travels with the attach payload. The runner keeps it in RAM
10955
+ * for the lifetime of the attach — on rebalance, edit, or restart the
10956
+ * orchestrator re-sends the latest snapshot. Engine is NOT carried: it is
10957
+ * node-local, resolved by the executing runner at dispatch time.
10863
10958
  */
10864
10959
  var RunnerCameraConfigSchema = object({
10865
10960
  deviceId: number(),
@@ -10910,14 +11005,11 @@ var RunnerCameraConfigSchema = object({
10910
11005
  */
10911
11006
  motionSources: MotionSourcesSchema.default(["analyzer"]),
10912
11007
  pipelineEnabled: boolean().default(true),
10913
- /** Engine choice for video steps (runtime+backend+format). */
10914
- engine: PipelineEngineChoiceSchema.optional(),
10915
11008
  /** Ordered tree of video steps. Absent → runner skips video detection. */
10916
11009
  steps: array(PipelineStepInputSchema).readonly().optional(),
10917
11010
  /** Audio classification branch. `enabled:false` disables, null skips. */
10918
11011
  audio: object({
10919
- engine: PipelineEngineChoiceSchema,
10920
- modelId: string(),
11012
+ modelId: string().optional(),
10921
11013
  enabled: boolean()
10922
11014
  }).nullable().optional(),
10923
11015
  /**
@@ -12298,7 +12390,9 @@ var AddonPageDeclarationSchema$1 = object({
12298
12390
  icon: string(),
12299
12391
  path: string(),
12300
12392
  remoteName: string(),
12301
- bundle: string()
12393
+ bundle: string(),
12394
+ section: string().optional(),
12395
+ sectionLabel: string().optional()
12302
12396
  });
12303
12397
  var AddonPageInfoSchema = object({
12304
12398
  addonId: string(),
@@ -12338,7 +12432,18 @@ var AddonPageDeclarationSchema = object({
12338
12432
  * the static-file route can compute an mtime-based cache-buster URL
12339
12433
  * without a separate filesystem stat.
12340
12434
  */
12341
- bundle: string()
12435
+ bundle: string(),
12436
+ /**
12437
+ * Sidebar section this page docks into. Well-known ids: `'detection'`,
12438
+ * `'cluster'`, `'administration'` — the page renders inside that group.
12439
+ * Any OTHER string creates (or joins) a custom section rendered after
12440
+ * the built-in groups; its label comes from `sectionLabel` (first
12441
+ * declaration wins), falling back to the id. Absent → the legacy
12442
+ * "Addon Pages" group.
12443
+ */
12444
+ section: string().optional(),
12445
+ /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
12446
+ sectionLabel: string().optional()
12342
12447
  });
12343
12448
  var addonPagesSourceCapability = {
12344
12449
  name: "addon-pages-source",
@@ -15182,11 +15287,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
15182
15287
  timestamp: number()
15183
15288
  });
15184
15289
  var CameraPipelineConfigSchema = object({
15185
- engine: PipelineEngineChoiceSchema,
15290
+ engine: PipelineEngineChoiceSchema.optional(),
15186
15291
  steps: array(PipelineStepInputSchema).readonly(),
15187
15292
  audio: object({
15188
- engine: PipelineEngineChoiceSchema,
15189
- modelId: string(),
15293
+ engine: PipelineEngineChoiceSchema.optional(),
15294
+ modelId: string().optional(),
15190
15295
  enabled: boolean(),
15191
15296
  settings: record(string(), unknown()).readonly().optional()
15192
15297
  }).nullable().optional()
@@ -15201,7 +15306,7 @@ var PipelineTemplateSchema = object({
15201
15306
  });
15202
15307
  var AgentAddonConfigSchema = object({
15203
15308
  enabled: boolean(),
15204
- modelId: string(),
15309
+ modelId: string().optional(),
15205
15310
  settings: record(string(), unknown()).readonly()
15206
15311
  });
15207
15312
  var AgentPipelineSettingsSchema = object({
@@ -15211,12 +15316,25 @@ var AgentPipelineSettingsSchema = object({
15211
15316
  detectWeight: number().positive().optional(),
15212
15317
  /** Node is eligible to run the detection pipeline (decode + inference). */
15213
15318
  detect: boolean().optional(),
15214
- /** Node is eligible to host decoder sessions. */
15319
+ /**
15320
+ * DEPRECATED AND IGNORED. Decode is always co-located with its frame
15321
+ * consumer, so decode eligibility IS detect eligibility. Kept optional in
15322
+ * the schema ONLY so persisted stores written before the removal still
15323
+ * parse — no code reads it and no write path emits it.
15324
+ */
15215
15325
  decode: boolean().optional(),
15216
15326
  /** Node is eligible to run audio-analyzer sessions. */
15217
15327
  audio: boolean().optional(),
15218
15328
  /** Node is eligible to be the ingest / source-owner (serve the restream). */
15219
- ingest: boolean().optional()
15329
+ ingest: boolean().optional(),
15330
+ /**
15331
+ * Operator override for the LAN host a cross-node decoder dials to reach
15332
+ * THIS node's restream (Cluster UI). Absent → auto-detect: a remote runner
15333
+ * falls back to its `CAMSTACK_HUB_URL`-derived host (the Moleculer address
15334
+ * it already uses to reach the hub). Set this only when the auto-detected
15335
+ * address is wrong (multi-homed host, NAT, custom interface).
15336
+ */
15337
+ reachableHost: string().optional()
15220
15338
  });
15221
15339
  var CameraPipelineForAgentSchema = object({
15222
15340
  steps: array(PipelineStepInputSchema).readonly(),
@@ -15264,25 +15382,6 @@ var PipelineAssignmentSchema = object({
15264
15382
  assignedAt: number()
15265
15383
  });
15266
15384
  /**
15267
- * Decoder placement record. Symmetric to `PipelineAssignmentSchema` but for
15268
- * the decoder-node placement domain (`balanceDecoder` decision: manual pin
15269
- * → co-located with pipeline → capacity).
15270
- */
15271
- var DecoderAssignmentSchema = object({
15272
- deviceId: number(),
15273
- /** Moleculer node id of the decoder provider currently responsible for this camera. */
15274
- decoderNodeId: string(),
15275
- /** True when the assignment was set manually via `assignDecoder`, false when chosen by the balancer. */
15276
- pinned: boolean(),
15277
- /** Why this assignment was made — useful for debugging the decoder balancer. */
15278
- reason: _enum([
15279
- "manual",
15280
- "co-located",
15281
- "capacity",
15282
- "hardware-affinity"
15283
- ])
15284
- });
15285
- /**
15286
15385
  * Per-agent load summary surfaced to the load balancer + dashboards.
15287
15386
  * Aggregated from each runner's `getLocalLoad` cap call.
15288
15387
  */
@@ -15322,6 +15421,15 @@ var GlobalMetricsSchema = object({
15322
15421
  * capability providers.
15323
15422
  */
15324
15423
  var CapabilityBindingsSchema = record(string(), string());
15424
+ /**
15425
+ * The cluster's single camera-source owner (`clusterRoles.ingestNode`) plus
15426
+ * its LAN-reachable host, if one is registered. See `getIngestOwner`.
15427
+ */
15428
+ var IngestOwnerSchema = object({
15429
+ ownerNodeId: string(),
15430
+ reachableHost: string().optional(),
15431
+ configIssue: string().optional()
15432
+ });
15325
15433
  /** Source block — always present; derives from the stream catalog. */
15326
15434
  var CameraSourceStatusSchema = object({ streams: array(object({
15327
15435
  camStreamId: string(),
@@ -15336,6 +15444,14 @@ var CameraAssignmentStatusSchema = object({
15336
15444
  detectionNodeId: string().nullable(),
15337
15445
  decoderNodeId: string().nullable(),
15338
15446
  audioNodeId: string().nullable(),
15447
+ /**
15448
+ * The node that OWNS this camera's physical source pull (dials the RTSP and
15449
+ * hosts the broker/restream) — the cluster ingest owner today
15450
+ * (`clusterRoles.ingestNode`), per-camera once source assignment lands. Lets
15451
+ * the UI show WHERE a camera is sourced without SSH/logs, and is the node the
15452
+ * broker block below was read from (pinned). Nullable only pre-wiring.
15453
+ */
15454
+ sourceNodeId: string().nullable(),
15339
15455
  pinned: object({
15340
15456
  detection: boolean(),
15341
15457
  decoder: boolean(),
@@ -15468,16 +15584,7 @@ method(object({
15468
15584
  }), object({ success: literal(true) }), {
15469
15585
  kind: "mutation",
15470
15586
  auth: "admin"
15471
- }), method(object({
15472
- deviceId: number(),
15473
- nodeId: string()
15474
- }), _void(), {
15475
- kind: "mutation",
15476
- auth: "admin"
15477
- }), method(object({ deviceId: number() }), _void(), {
15478
- kind: "mutation",
15479
- auth: "admin"
15480
- }), method(_void(), array(DecoderAssignmentSchema).readonly()), method(object({
15587
+ }), method(_void(), IngestOwnerSchema), method(object({
15481
15588
  deviceId: number(),
15482
15589
  nodeId: string()
15483
15590
  }), object({ success: literal(true) }), {
@@ -15498,10 +15605,7 @@ method(object({
15498
15605
  nodeId: string(),
15499
15606
  pinned: boolean(),
15500
15607
  assignedAt: number()
15501
- }))), method(object({
15502
- deviceId: number(),
15503
- pipelineNodeId: string().optional()
15504
- }), DecoderAssignmentSchema), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
15608
+ }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
15505
15609
  nodeId: string(),
15506
15610
  settings: AgentPipelineSettingsSchema
15507
15611
  })).readonly()), method(object({
@@ -15531,12 +15635,26 @@ method(object({
15531
15635
  }), method(object({
15532
15636
  agentNodeId: string(),
15533
15637
  detect: boolean().nullable().optional(),
15534
- decode: boolean().nullable().optional(),
15535
15638
  audio: boolean().nullable().optional(),
15536
15639
  ingest: boolean().nullable().optional()
15537
15640
  }), object({ success: literal(true) }), {
15538
15641
  kind: "mutation",
15539
15642
  auth: "admin"
15643
+ }), method(object({
15644
+ agentNodeId: string(),
15645
+ reachableHost: string().nullable()
15646
+ }), object({ success: literal(true) }), {
15647
+ kind: "mutation",
15648
+ auth: "admin"
15649
+ }), method(object({ agentNodeId: string() }), object({
15650
+ success: literal(true),
15651
+ /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
15652
+ effectiveModelId: string().nullable(),
15653
+ /** Number of cameras whose node-scoped overrides were cleared. */
15654
+ clearedCameraOverrides: number()
15655
+ }), {
15656
+ kind: "mutation",
15657
+ auth: "admin"
15540
15658
  }), method(object({ deviceId: number() }), CameraPipelineSettingsSchema.nullable()), method(object({
15541
15659
  deviceId: number(),
15542
15660
  addonId: string(),
@@ -15581,22 +15699,6 @@ method(object({
15581
15699
  kind: "mutation",
15582
15700
  auth: "admin"
15583
15701
  });
15584
- var RegisteredStreamSchema = object({
15585
- streamId: string(),
15586
- label: string().optional(),
15587
- codec: string(),
15588
- type: _enum(["video", "audio"]),
15589
- sourceUrl: string()
15590
- });
15591
- var ExposedResourceSchema = object({
15592
- streamId: string(),
15593
- format: string(),
15594
- value: string()
15595
- });
15596
- method(object({
15597
- deviceId: number(),
15598
- streams: array(RegisteredStreamSchema).readonly()
15599
- }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), array(ExposedResourceSchema).readonly());
15600
15702
  /**
15601
15703
  * Query filter for settings-store collections.
15602
15704
  */
@@ -15749,9 +15851,9 @@ method(SendEmailInputSchema, SendEmailResultSchema, {
15749
15851
  /**
15750
15852
  * A single device snapshot returned as base64 JPEG/PNG.
15751
15853
  *
15752
- * Shared with the `snapshot-provider` collection cap the orchestrator
15753
- * receives the same shape from each native provider and from the
15754
- * broker-based fallback.
15854
+ * The `SnapshotAddon` wrapper returns this shape whether the frame came from
15855
+ * the device-native provider (onboard capture) or from the stream-broker
15856
+ * prebuffer fallback.
15755
15857
  */
15756
15858
  var SnapshotImageSchema = object({
15757
15859
  base64: string(),
@@ -15783,10 +15885,6 @@ DeviceType.Camera, method(object({
15783
15885
  kind: "mutation",
15784
15886
  auth: "admin"
15785
15887
  });
15786
- method(object({ deviceId: number() }), boolean()), method(object({
15787
- deviceId: number(),
15788
- streamId: string().optional()
15789
- }), SnapshotImageSchema.nullable());
15790
15888
  /**
15791
15889
  * `sso-bridge` — internal hub-only cap that lets SSO-style auth
15792
15890
  * providers (OIDC, SAML, magic-link, …) mint an HMAC-signed token
@@ -16147,9 +16245,10 @@ method(object({
16147
16245
  auth: "admin"
16148
16246
  });
16149
16247
  /**
16150
- * Optional client-side hints sent at session creation to help the
16151
- * provider pick the best native source. All fields are optional —
16152
- * a viewer that knows nothing still gets a sane default.
16248
+ * Optional client-side hints sent at session creation to help the provider
16249
+ * pick the best native source. All fields optional — a viewer that knows
16250
+ * nothing still gets a sane default. (Relocated from the retired `webrtc`
16251
+ * collection cap; this `webrtc-session` cap is the live signaling surface.)
16153
16252
  */
16154
16253
  var webrtcClientHintsSchema = object({
16155
16254
  viewportWidth: number().int().positive().optional(),
@@ -16160,22 +16259,6 @@ var webrtcClientHintsSchema = object({
16160
16259
  /** Hard tier override; takes precedence over scoring when registered. */
16161
16260
  prefersTier: string().optional()
16162
16261
  }).partial();
16163
- method(object({
16164
- streamId: string(),
16165
- sdpOffer: string()
16166
- }), string(), { kind: "mutation" }), method(object({ streamId: string() }), boolean()), method(object({
16167
- streamId: string(),
16168
- codec: string()
16169
- }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), _void(), { kind: "mutation" }), method(object({
16170
- streamId: string(),
16171
- hints: webrtcClientHintsSchema.optional()
16172
- }), object({
16173
- sessionId: string(),
16174
- sdpOffer: string()
16175
- }), { kind: "mutation" }), method(object({
16176
- sessionId: string(),
16177
- sdpAnswer: string()
16178
- }), _void(), { kind: "mutation" }), method(object({ sessionId: string() }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), boolean());
16179
16262
  /**
16180
16263
  * Discriminated target for a WebRTC session. The client sends this
16181
16264
  * structured object instead of building / parsing brokerId strings;
@@ -16662,7 +16745,15 @@ var FrameworkPackageStatusSchema = object({
16662
16745
  latestVersion: string().nullable(),
16663
16746
  hasUpdate: boolean(),
16664
16747
  /** Optional manifest description for the row tooltip. */
16665
- description: string().optional()
16748
+ description: string().optional(),
16749
+ /**
16750
+ * Content build-id (md5 of the resolved `dist/` tree) of the code the hub
16751
+ * ACTUALLY loaded. Framework packages ship code changes without always
16752
+ * bumping `currentVersion`, so semver alone hides "same version, new code".
16753
+ * `null` when the dist can't be hashed (not installed / empty). The admin-UI
16754
+ * surfaces this so a stale-code hub is visible even at an unchanged version.
16755
+ */
16756
+ buildId: string().nullable()
16666
16757
  });
16667
16758
  var LogStreamEntrySchema = object({
16668
16759
  timestamp: string(),
@@ -21048,23 +21139,23 @@ Object.freeze({
21048
21139
  addonId: null,
21049
21140
  access: "create"
21050
21141
  },
21051
- "pipelineExecutor.deleteModel": {
21142
+ "pipelineExecutor.clearDeviceOverrides": {
21052
21143
  capName: "pipeline-executor",
21053
21144
  capScope: "system",
21054
21145
  addonId: null,
21055
21146
  access: "delete"
21056
21147
  },
21057
- "pipelineExecutor.deleteTemplate": {
21148
+ "pipelineExecutor.deleteModel": {
21058
21149
  capName: "pipeline-executor",
21059
21150
  capScope: "system",
21060
21151
  addonId: null,
21061
21152
  access: "delete"
21062
21153
  },
21063
- "pipelineExecutor.detect": {
21154
+ "pipelineExecutor.deleteTemplate": {
21064
21155
  capName: "pipeline-executor",
21065
21156
  capScope: "system",
21066
21157
  addonId: null,
21067
- access: "view"
21158
+ access: "delete"
21068
21159
  },
21069
21160
  "pipelineExecutor.downloadModel": {
21070
21161
  capName: "pipeline-executor",
@@ -21258,13 +21349,13 @@ Object.freeze({
21258
21349
  addonId: null,
21259
21350
  access: "create"
21260
21351
  },
21261
- "pipelineOrchestrator.assignAudio": {
21262
- capName: "pipeline-orchestrator",
21352
+ "pipelineExecutor.validatePipeline": {
21353
+ capName: "pipeline-executor",
21263
21354
  capScope: "system",
21264
21355
  addonId: null,
21265
- access: "create"
21356
+ access: "view"
21266
21357
  },
21267
- "pipelineOrchestrator.assignDecoder": {
21358
+ "pipelineOrchestrator.assignAudio": {
21268
21359
  capName: "pipeline-orchestrator",
21269
21360
  capScope: "system",
21270
21361
  addonId: null,
@@ -21348,19 +21439,13 @@ Object.freeze({
21348
21439
  addonId: null,
21349
21440
  access: "view"
21350
21441
  },
21351
- "pipelineOrchestrator.getDecoderAssignment": {
21352
- capName: "pipeline-orchestrator",
21353
- capScope: "system",
21354
- addonId: null,
21355
- access: "view"
21356
- },
21357
- "pipelineOrchestrator.getDecoderAssignments": {
21442
+ "pipelineOrchestrator.getGlobalMetrics": {
21358
21443
  capName: "pipeline-orchestrator",
21359
21444
  capScope: "system",
21360
21445
  addonId: null,
21361
21446
  access: "view"
21362
21447
  },
21363
- "pipelineOrchestrator.getGlobalMetrics": {
21448
+ "pipelineOrchestrator.getIngestOwner": {
21364
21449
  capName: "pipeline-orchestrator",
21365
21450
  capScope: "system",
21366
21451
  addonId: null,
@@ -21402,6 +21487,12 @@ Object.freeze({
21402
21487
  addonId: null,
21403
21488
  access: "delete"
21404
21489
  },
21490
+ "pipelineOrchestrator.resetNodePipelineDefaults": {
21491
+ capName: "pipeline-orchestrator",
21492
+ capScope: "system",
21493
+ addonId: null,
21494
+ access: "delete"
21495
+ },
21405
21496
  "pipelineOrchestrator.resolvePipeline": {
21406
21497
  capName: "pipeline-orchestrator",
21407
21498
  capScope: "system",
@@ -21438,37 +21529,37 @@ Object.freeze({
21438
21529
  addonId: null,
21439
21530
  access: "create"
21440
21531
  },
21441
- "pipelineOrchestrator.setCameraPipelineForAgent": {
21532
+ "pipelineOrchestrator.setAgentReachableHost": {
21442
21533
  capName: "pipeline-orchestrator",
21443
21534
  capScope: "system",
21444
21535
  addonId: null,
21445
21536
  access: "create"
21446
21537
  },
21447
- "pipelineOrchestrator.setCameraStepOverride": {
21538
+ "pipelineOrchestrator.setCameraPipelineForAgent": {
21448
21539
  capName: "pipeline-orchestrator",
21449
21540
  capScope: "system",
21450
21541
  addonId: null,
21451
21542
  access: "create"
21452
21543
  },
21453
- "pipelineOrchestrator.setCameraStepToggle": {
21544
+ "pipelineOrchestrator.setCameraStepOverride": {
21454
21545
  capName: "pipeline-orchestrator",
21455
21546
  capScope: "system",
21456
21547
  addonId: null,
21457
21548
  access: "create"
21458
21549
  },
21459
- "pipelineOrchestrator.setCapabilityBinding": {
21550
+ "pipelineOrchestrator.setCameraStepToggle": {
21460
21551
  capName: "pipeline-orchestrator",
21461
21552
  capScope: "system",
21462
21553
  addonId: null,
21463
21554
  access: "create"
21464
21555
  },
21465
- "pipelineOrchestrator.unassignAudio": {
21556
+ "pipelineOrchestrator.setCapabilityBinding": {
21466
21557
  capName: "pipeline-orchestrator",
21467
21558
  capScope: "system",
21468
21559
  addonId: null,
21469
21560
  access: "create"
21470
21561
  },
21471
- "pipelineOrchestrator.unassignDecoder": {
21562
+ "pipelineOrchestrator.unassignAudio": {
21472
21563
  capName: "pipeline-orchestrator",
21473
21564
  capScope: "system",
21474
21565
  addonId: null,
@@ -21768,24 +21859,6 @@ Object.freeze({
21768
21859
  addonId: null,
21769
21860
  access: "create"
21770
21861
  },
21771
- "restreamer.getExposedResources": {
21772
- capName: "restreamer",
21773
- capScope: "system",
21774
- addonId: null,
21775
- access: "view"
21776
- },
21777
- "restreamer.registerDevice": {
21778
- capName: "restreamer",
21779
- capScope: "system",
21780
- addonId: null,
21781
- access: "create"
21782
- },
21783
- "restreamer.unregisterDevice": {
21784
- capName: "restreamer",
21785
- capScope: "system",
21786
- addonId: null,
21787
- access: "delete"
21788
- },
21789
21862
  "scriptRunner.run": {
21790
21863
  capName: "script-runner",
21791
21864
  capScope: "device",
@@ -21888,18 +21961,6 @@ Object.freeze({
21888
21961
  addonId: null,
21889
21962
  access: "create"
21890
21963
  },
21891
- "snapshotProvider.getSnapshot": {
21892
- capName: "snapshot-provider",
21893
- capScope: "system",
21894
- addonId: null,
21895
- access: "view"
21896
- },
21897
- "snapshotProvider.supportsDevice": {
21898
- capName: "snapshot-provider",
21899
- capScope: "system",
21900
- addonId: null,
21901
- access: "view"
21902
- },
21903
21964
  "ssoBridge.signBridgeToken": {
21904
21965
  capName: "sso-bridge",
21905
21966
  capScope: "system",
@@ -22326,30 +22387,6 @@ Object.freeze({
22326
22387
  addonId: null,
22327
22388
  access: "view"
22328
22389
  },
22329
- "streamingEngine.getStreamUrl": {
22330
- capName: "streaming-engine",
22331
- capScope: "system",
22332
- addonId: null,
22333
- access: "view"
22334
- },
22335
- "streamingEngine.listStreams": {
22336
- capName: "streaming-engine",
22337
- capScope: "system",
22338
- addonId: null,
22339
- access: "view"
22340
- },
22341
- "streamingEngine.registerStream": {
22342
- capName: "streaming-engine",
22343
- capScope: "system",
22344
- addonId: null,
22345
- access: "create"
22346
- },
22347
- "streamingEngine.unregisterStream": {
22348
- capName: "streaming-engine",
22349
- capScope: "system",
22350
- addonId: null,
22351
- access: "delete"
22352
- },
22353
22390
  "streamParams.getConfigSchema": {
22354
22391
  capName: "stream-params",
22355
22392
  capScope: "device",
@@ -22716,54 +22753,6 @@ Object.freeze({
22716
22753
  addonId: null,
22717
22754
  access: "create"
22718
22755
  },
22719
- "webrtc.closeSession": {
22720
- capName: "webrtc",
22721
- capScope: "system",
22722
- addonId: null,
22723
- access: "create"
22724
- },
22725
- "webrtc.createSession": {
22726
- capName: "webrtc",
22727
- capScope: "system",
22728
- addonId: null,
22729
- access: "create"
22730
- },
22731
- "webrtc.handleAnswer": {
22732
- capName: "webrtc",
22733
- capScope: "system",
22734
- addonId: null,
22735
- access: "create"
22736
- },
22737
- "webrtc.handleOffer": {
22738
- capName: "webrtc",
22739
- capScope: "system",
22740
- addonId: null,
22741
- access: "create"
22742
- },
22743
- "webrtc.hasAdaptiveBitrate": {
22744
- capName: "webrtc",
22745
- capScope: "system",
22746
- addonId: null,
22747
- access: "view"
22748
- },
22749
- "webrtc.registerStream": {
22750
- capName: "webrtc",
22751
- capScope: "system",
22752
- addonId: null,
22753
- access: "create"
22754
- },
22755
- "webrtc.supportsStream": {
22756
- capName: "webrtc",
22757
- capScope: "system",
22758
- addonId: null,
22759
- access: "view"
22760
- },
22761
- "webrtc.unregisterStream": {
22762
- capName: "webrtc",
22763
- capScope: "system",
22764
- addonId: null,
22765
- access: "delete"
22766
- },
22767
22756
  "webrtcSession.addIceCandidate": {
22768
22757
  capName: "webrtc-session",
22769
22758
  capScope: "device",
@@ -23277,7 +23266,8 @@ var ModelStudioAddon = class extends BaseAddon {
23277
23266
  icon: "boxes",
23278
23267
  path: "/addon/model-studio",
23279
23268
  remoteName: "addon_model_studio_page",
23280
- bundle: "remoteEntry.js"
23269
+ bundle: "remoteEntry.js",
23270
+ section: "detection"
23281
23271
  }];
23282
23272
  constructor() {
23283
23273
  super({});