@camstack/types 1.2.308 → 1.2.310

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 (39) hide show
  1. package/dist/addon.js +1 -1
  2. package/dist/addon.mjs +1 -1
  3. package/dist/capabilities/custom-model-registry.cap.d.ts +38 -4
  4. package/dist/capabilities/device-manager.cap.d.ts +8 -8
  5. package/dist/capabilities/hub-address.d.ts +123 -0
  6. package/dist/capabilities/index.d.ts +4 -3
  7. package/dist/capabilities/local-network.cap.d.ts +605 -11
  8. package/dist/capabilities/model-convert.cap.d.ts +19 -2
  9. package/dist/capabilities/model-distributor.cap.d.ts +38 -4
  10. package/dist/capabilities/pipeline-executor.cap.d.ts +124 -10
  11. package/dist/capabilities/pipeline-runner.cap.d.ts +13 -0
  12. package/dist/capabilities/webrtc-session.cap.d.ts +36 -0
  13. package/dist/catalogs/frigate-plus-classmap.d.ts +10 -7
  14. package/dist/catalogs/index.d.ts +1 -1
  15. package/dist/catalogs/root-model-supply.d.ts +202 -0
  16. package/dist/client.js +55 -49
  17. package/dist/client.mjs +55 -49
  18. package/dist/device/location-icon.d.ts +4 -4
  19. package/dist/device/site-zone.d.ts +2 -2
  20. package/dist/ffmpeg/pinned-spawn.d.ts +1 -1
  21. package/dist/generated/addon-api.d.ts +40 -0
  22. package/dist/generated/method-access-map.d.ts +1 -1
  23. package/dist/generated/method-device-selectors.d.ts +1 -1
  24. package/dist/generated/system-proxy.d.ts +1 -1
  25. package/dist/index.d.ts +2 -1
  26. package/dist/index.js +1351 -641
  27. package/dist/index.mjs +1310 -629
  28. package/dist/interfaces/event-bus.d.ts +12 -6
  29. package/dist/interfaces/pipeline-executor-capability.d.ts +6 -1
  30. package/dist/node.js +1 -1
  31. package/dist/node.mjs +1 -1
  32. package/dist/pipeline/root-face-crop.d.ts +31 -0
  33. package/dist/{sleep-DXKLNFvA.mjs → sleep-C16NCZR2.mjs} +1 -0
  34. package/dist/{sleep-C3gDpn7G.js → sleep-DyF95STh.js} +1 -0
  35. package/dist/types/detection.d.ts +9 -0
  36. package/dist/types/models.d.ts +69 -8
  37. package/dist/types/pipeline-step.d.ts +6 -0
  38. package/package.json +1 -1
  39. package/dist/catalogs/classifier-fill.d.ts +0 -104
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-C3gDpn7G.js");
2
+ const require_sleep = require("./sleep-DyF95STh.js");
3
3
  const require_event_category = require("./event-category-DmS_tLsU.js");
4
4
  const require_composition = require("./composition-BLWAivB2.js");
5
5
  const require_err_msg = require("./err-msg-Dx2Kor0g.js");
@@ -5346,424 +5346,40 @@ function resolveClipSearchMinScore(meta, request) {
5346
5346
  return Math.min(1, Math.round(meta.searchMinScore * factor * 1e3) / 1e3);
5347
5347
  }
5348
5348
  //#endregion
5349
- //#region src/catalogs/coco-classmap.ts
5350
- var COCO_80_LABELS = [
5351
- {
5352
- id: "person",
5353
- name: "Person"
5354
- },
5355
- {
5356
- id: "bicycle",
5357
- name: "Bicycle"
5358
- },
5359
- {
5360
- id: "car",
5361
- name: "Car"
5362
- },
5363
- {
5364
- id: "motorcycle",
5365
- name: "Motorcycle"
5366
- },
5367
- {
5368
- id: "airplane",
5369
- name: "Airplane"
5370
- },
5371
- {
5372
- id: "bus",
5373
- name: "Bus"
5374
- },
5375
- {
5376
- id: "train",
5377
- name: "Train"
5378
- },
5379
- {
5380
- id: "truck",
5381
- name: "Truck"
5382
- },
5383
- {
5384
- id: "boat",
5385
- name: "Boat"
5386
- },
5387
- {
5388
- id: "traffic light",
5389
- name: "Traffic Light"
5390
- },
5391
- {
5392
- id: "fire hydrant",
5393
- name: "Fire Hydrant"
5394
- },
5395
- {
5396
- id: "stop sign",
5397
- name: "Stop Sign"
5398
- },
5399
- {
5400
- id: "parking meter",
5401
- name: "Parking Meter"
5402
- },
5403
- {
5404
- id: "bench",
5405
- name: "Bench"
5406
- },
5407
- {
5408
- id: "bird",
5409
- name: "Bird"
5410
- },
5411
- {
5412
- id: "cat",
5413
- name: "Cat"
5414
- },
5415
- {
5416
- id: "dog",
5417
- name: "Dog"
5418
- },
5419
- {
5420
- id: "horse",
5421
- name: "Horse"
5422
- },
5423
- {
5424
- id: "sheep",
5425
- name: "Sheep"
5426
- },
5427
- {
5428
- id: "cow",
5429
- name: "Cow"
5430
- },
5431
- {
5432
- id: "elephant",
5433
- name: "Elephant"
5434
- },
5435
- {
5436
- id: "bear",
5437
- name: "Bear"
5438
- },
5439
- {
5440
- id: "zebra",
5441
- name: "Zebra"
5442
- },
5443
- {
5444
- id: "giraffe",
5445
- name: "Giraffe"
5446
- },
5447
- {
5448
- id: "backpack",
5449
- name: "Backpack"
5450
- },
5451
- {
5452
- id: "umbrella",
5453
- name: "Umbrella"
5454
- },
5455
- {
5456
- id: "handbag",
5457
- name: "Handbag"
5458
- },
5459
- {
5460
- id: "tie",
5461
- name: "Tie"
5462
- },
5463
- {
5464
- id: "suitcase",
5465
- name: "Suitcase"
5466
- },
5467
- {
5468
- id: "frisbee",
5469
- name: "Frisbee"
5470
- },
5471
- {
5472
- id: "skis",
5473
- name: "Skis"
5474
- },
5475
- {
5476
- id: "snowboard",
5477
- name: "Snowboard"
5478
- },
5479
- {
5480
- id: "sports ball",
5481
- name: "Sports Ball"
5482
- },
5483
- {
5484
- id: "kite",
5485
- name: "Kite"
5486
- },
5487
- {
5488
- id: "baseball bat",
5489
- name: "Baseball Bat"
5490
- },
5491
- {
5492
- id: "baseball glove",
5493
- name: "Baseball Glove"
5494
- },
5495
- {
5496
- id: "skateboard",
5497
- name: "Skateboard"
5498
- },
5499
- {
5500
- id: "surfboard",
5501
- name: "Surfboard"
5502
- },
5503
- {
5504
- id: "tennis racket",
5505
- name: "Tennis Racket"
5506
- },
5507
- {
5508
- id: "bottle",
5509
- name: "Bottle"
5510
- },
5511
- {
5512
- id: "wine glass",
5513
- name: "Wine Glass"
5514
- },
5515
- {
5516
- id: "cup",
5517
- name: "Cup"
5518
- },
5519
- {
5520
- id: "fork",
5521
- name: "Fork"
5522
- },
5523
- {
5524
- id: "knife",
5525
- name: "Knife"
5526
- },
5527
- {
5528
- id: "spoon",
5529
- name: "Spoon"
5530
- },
5531
- {
5532
- id: "bowl",
5533
- name: "Bowl"
5534
- },
5535
- {
5536
- id: "banana",
5537
- name: "Banana"
5538
- },
5539
- {
5540
- id: "apple",
5541
- name: "Apple"
5542
- },
5543
- {
5544
- id: "sandwich",
5545
- name: "Sandwich"
5546
- },
5547
- {
5548
- id: "orange",
5549
- name: "Orange"
5550
- },
5551
- {
5552
- id: "broccoli",
5553
- name: "Broccoli"
5554
- },
5555
- {
5556
- id: "carrot",
5557
- name: "Carrot"
5558
- },
5559
- {
5560
- id: "hot dog",
5561
- name: "Hot Dog"
5562
- },
5563
- {
5564
- id: "pizza",
5565
- name: "Pizza"
5566
- },
5567
- {
5568
- id: "donut",
5569
- name: "Donut"
5570
- },
5571
- {
5572
- id: "cake",
5573
- name: "Cake"
5574
- },
5575
- {
5576
- id: "chair",
5577
- name: "Chair"
5578
- },
5579
- {
5580
- id: "couch",
5581
- name: "Couch"
5582
- },
5583
- {
5584
- id: "potted plant",
5585
- name: "Potted Plant"
5586
- },
5587
- {
5588
- id: "bed",
5589
- name: "Bed"
5590
- },
5591
- {
5592
- id: "dining table",
5593
- name: "Dining Table"
5594
- },
5595
- {
5596
- id: "toilet",
5597
- name: "Toilet"
5598
- },
5599
- {
5600
- id: "tv",
5601
- name: "TV"
5602
- },
5603
- {
5604
- id: "laptop",
5605
- name: "Laptop"
5606
- },
5607
- {
5608
- id: "mouse",
5609
- name: "Mouse"
5610
- },
5611
- {
5612
- id: "remote",
5613
- name: "Remote"
5614
- },
5615
- {
5616
- id: "keyboard",
5617
- name: "Keyboard"
5618
- },
5619
- {
5620
- id: "cell phone",
5621
- name: "Cell Phone"
5622
- },
5623
- {
5624
- id: "microwave",
5625
- name: "Microwave"
5626
- },
5627
- {
5628
- id: "oven",
5629
- name: "Oven"
5630
- },
5631
- {
5632
- id: "toaster",
5633
- name: "Toaster"
5634
- },
5635
- {
5636
- id: "sink",
5637
- name: "Sink"
5638
- },
5639
- {
5640
- id: "refrigerator",
5641
- name: "Refrigerator"
5642
- },
5643
- {
5644
- id: "book",
5645
- name: "Book"
5646
- },
5647
- {
5648
- id: "clock",
5649
- name: "Clock"
5650
- },
5651
- {
5652
- id: "vase",
5653
- name: "Vase"
5654
- },
5655
- {
5656
- id: "scissors",
5657
- name: "Scissors"
5658
- },
5659
- {
5660
- id: "teddy bear",
5661
- name: "Teddy Bear"
5662
- },
5663
- {
5664
- id: "hair drier",
5665
- name: "Hair Drier"
5666
- },
5667
- {
5668
- id: "toothbrush",
5669
- name: "Toothbrush"
5670
- }
5671
- ];
5672
- var MACRO_LABELS = [
5673
- {
5674
- id: "person",
5675
- name: "Person"
5676
- },
5677
- {
5678
- id: "vehicle",
5679
- name: "Vehicle"
5680
- },
5681
- {
5682
- id: "animal",
5683
- name: "Animal"
5684
- },
5685
- {
5686
- id: "package",
5687
- name: "Package"
5688
- }
5689
- ];
5690
- /**
5691
- * The class names a per-class COUNT can ever be keyed on — the macro ids, and
5692
- * only those.
5693
- *
5694
- * `preserveOriginal: false` below is the whole reason this set exists: the
5695
- * executor maps each raw COCO label onto its macro and DROPS everything else,
5696
- * so a tracked detection's `className` is always one of {@link MACRO_LABELS}.
5697
- * Any surface that indexes counts by class name — the ZoneAnalytics occupancy
5698
- * snapshot's `byClass`, and therefore every occupancy rule — can match nothing
5699
- * else. Derived from the catalog, never hand-listed, so a new macro joins it by
5700
- * being declared once.
5701
- *
5702
- * Cost of not having it, 2026-08-12: the notification rule editor offered the
5703
- * full taxonomy for an occupancy class (occupancy is the one condition that
5704
- * does not go through the engine's macro-expanding `expandClassSelector`), two
5705
- * live rules were authored on `car`, and both counters read 0 forever.
5706
- */
5707
- var DETECTION_MACRO_CLASSES = new Set(MACRO_LABELS.map((l) => l.id));
5708
- /** True when `className` is a macro the detector can actually emit (and hence a
5709
- * class a count can be keyed on) — see {@link DETECTION_MACRO_CLASSES}. */
5710
- function isDetectionMacroClass(className) {
5711
- return DETECTION_MACRO_CLASSES.has(className.trim().toLowerCase());
5712
- }
5713
- var COCO_TO_MACRO = {
5714
- mapping: {
5715
- person: "person",
5716
- bicycle: "vehicle",
5717
- car: "vehicle",
5718
- motorcycle: "vehicle",
5719
- airplane: "vehicle",
5720
- bus: "vehicle",
5721
- train: "vehicle",
5722
- truck: "vehicle",
5723
- boat: "vehicle",
5724
- bird: "animal",
5725
- cat: "animal",
5726
- dog: "animal",
5727
- horse: "animal",
5728
- sheep: "animal",
5729
- cow: "animal",
5730
- elephant: "animal",
5731
- bear: "animal",
5732
- zebra: "animal",
5733
- giraffe: "animal",
5734
- suitcase: "package",
5735
- backpack: "package",
5736
- handbag: "package"
5737
- },
5738
- preserveOriginal: false
5739
- };
5740
- //#endregion
5741
- //#region src/catalogs/classifier-fill.ts
5742
- /** Steps a root detector may declare it fills. */
5743
- var FILLABLE_STEP_IDS = [
5349
+ //#region src/catalogs/root-model-supply.ts
5350
+ /** Steps whose effect a root label can BE. */
5351
+ var LABEL_SUPPLY_STEP_IDS = [
5744
5352
  "animal-classifier",
5745
5353
  "vehicle-classifier",
5746
5354
  "package-detection"
5747
5355
  ];
5748
- /** Narrow a step id to a fillable one — type guard, not a cast. */
5749
- function isFillableStepId(value) {
5750
- return FILLABLE_STEP_IDS.some((id) => id === value);
5356
+ /** Steps whose box a root detection can BE. */
5357
+ var CROP_SUPPLY_STEP_IDS = ["plate-detection", "face-detection"];
5358
+ /** Every step a root model can supply. */
5359
+ var ROOT_SUPPLIABLE_STEP_IDS = [...LABEL_SUPPLY_STEP_IDS, ...CROP_SUPPLY_STEP_IDS];
5360
+ /** Narrow a step id to a suppliable one — type guard, not a cast. */
5361
+ function isRootSuppliableStepId(value) {
5362
+ return ROOT_SUPPLIABLE_STEP_IDS.some((id) => id === value);
5363
+ }
5364
+ /** Narrow a step id to a label-kind one. */
5365
+ function isLabelSupplyStepId(value) {
5366
+ return LABEL_SUPPLY_STEP_IDS.some((id) => id === value);
5751
5367
  }
5752
5368
  /**
5753
- * The EFFECT vocabulary of each fillable step: exactly the strings the step
5369
+ * The EFFECT vocabulary of each label-kind step: exactly the strings the step
5754
5370
  * itself emits, in its own spelling. A linked label is one of these and nothing
5755
5371
  * else, so a tier-1 value means the same thing whichever writer produced it.
5756
5372
  *
5757
5373
  * - `animal-classifier`: the PRIMARY model's `-labels.json` (Open Images V7,
5758
5374
  * 8 classes) — also the species allow-list the step offers.
5759
- * - `vehicle-classifier`: `vehicle-type-v1-labels.json`, capitalised as the
5760
- * model emits them (the classifier writes `winner.class` verbatim).
5375
+ * - `vehicle-classifier`: the classifier's labels, capitalised as it emits them
5376
+ * (it writes `winner.class` verbatim).
5761
5377
  * - `package-detection`: its one output class.
5762
5378
  *
5763
- * `scripts/check-label-link-vocabulary.ts` fails the build if a model entry's
5379
+ * `scripts/check-label-link-vocabulary.ts` fails the build if a declared
5764
5380
  * `labelToEffect` names anything outside this table.
5765
5381
  */
5766
- var FILLABLE_STEP_VOCABULARIES = {
5382
+ var LABEL_SUPPLY_VOCABULARIES = {
5767
5383
  "animal-classifier": [
5768
5384
  "bird",
5769
5385
  "cat",
@@ -5786,8 +5402,8 @@ var FILLABLE_STEP_VOCABULARIES = {
5786
5402
  ],
5787
5403
  "package-detection": ["package"]
5788
5404
  };
5789
- /** The macro a detection must carry for the step to apply to it. */
5790
- var FILLABLE_STEP_MACRO = {
5405
+ /** The macro a detection must carry for a label-kind step to apply to it. */
5406
+ var LABEL_SUPPLY_MACRO = {
5791
5407
  "animal-classifier": "animal",
5792
5408
  "vehicle-classifier": "vehicle",
5793
5409
  "package-detection": "package"
@@ -5804,12 +5420,46 @@ var EFFECT_ALIASES = {
5804
5420
  },
5805
5421
  "package-detection": {}
5806
5422
  };
5807
- /** The policy values of a step's `labelLink` setting. */
5808
- var LABEL_LINK_POLICIES = ["run", "skip-when-filled"];
5809
- /** Absent setting ⇒ this. It changes nothing unless the root model declares a fill. */
5810
- var DEFAULT_LABEL_LINK_POLICY = "skip-when-filled";
5811
- /** The step-settings key the policy is stored under (DeviceStepConfig.settings). */
5812
- var LABEL_LINK_SETTING_KEY = "labelLink";
5423
+ var CROP_SUPPLY_SOURCES = {
5424
+ "plate-detection": {
5425
+ rootLabels: ["license_plate"],
5426
+ parentMacro: "vehicle",
5427
+ childClass: "plate",
5428
+ servedChildSteps: ["plate-ocr"],
5429
+ use: "as-output"
5430
+ },
5431
+ "face-detection": {
5432
+ rootLabels: ["face"],
5433
+ parentMacro: "person",
5434
+ childClass: "face",
5435
+ servedChildSteps: ["face-embedding"],
5436
+ use: "refine",
5437
+ minRootInputPx: 640
5438
+ }
5439
+ };
5440
+ /**
5441
+ * Whether an operator intends a step to take the root's output when the
5442
+ * root declares it — the default of the `useRootModel` flag. The label kinds
5443
+ * default ON: the detector's answer is the cheaper one and the classifier
5444
+ * keeps its model for the day the root changes. The crop kinds default OFF: a
5445
+ * root plate box has an uncalibrated OCR margin, and root-face refinement has
5446
+ * not run on live traffic yet.
5447
+ */
5448
+ var USE_ROOT_MODEL_DEFAULTS = {
5449
+ "animal-classifier": true,
5450
+ "vehicle-classifier": true,
5451
+ "package-detection": true,
5452
+ "plate-detection": false,
5453
+ "face-detection": false
5454
+ };
5455
+ /** The step-settings key the flag is stored under (DeviceStepConfig.settings, camera overrides). */
5456
+ var USE_ROOT_MODEL_SETTING_KEY = "useRootModel";
5457
+ /**
5458
+ * The retired D715 policy key. A `skip-when-filled` / `run` value still
5459
+ * stored under it reads as the flag (`true` / `false`) when no flag is stored
5460
+ * — the normal read path, not a migration.
5461
+ */
5462
+ var RETIRED_LABEL_LINK_KEY = "labelLink";
5813
5463
  /** `ScoredLabel.stepId` prefix of a label the detector wrote for a step. */
5814
5464
  var LINKED_LABEL_STEP_PREFIX = "detector-link:";
5815
5465
  /** `detector-link:<stepId>` — the attribution of a linked label. */
@@ -5821,91 +5471,175 @@ function linkedStepOf(attributionStepId) {
5821
5471
  if (attributionStepId === void 0) return void 0;
5822
5472
  if (!attributionStepId.startsWith("detector-link:")) return void 0;
5823
5473
  const stepId = attributionStepId.slice(14);
5824
- return isFillableStepId(stepId) ? stepId : void 0;
5825
- }
5826
- /**
5827
- * A labelmap is COCO when it carries at least half of the COCO-80 classes. A
5828
- * COCO detector NEVER fills a step (operator decision, D715): its `dog` / `car`
5829
- * is the generic class the classifier exists to refine, and its six animal
5830
- * classes would otherwise read as "fills animal-classifier 6/8". Frigate+
5831
- * shares about a dozen COCO names; a COCO-80 or COCO-91 labelmap carries ~80.
5832
- */
5833
- function isCocoLabelmap(labels) {
5834
- const present = new Set(labels);
5835
- return COCO_80_LABELS.filter((l) => present.has(l.id)).length * 2 >= COCO_80_LABELS.length;
5474
+ return isLabelSupplyStepId(stepId) ? stepId : void 0;
5836
5475
  }
5837
5476
  function effectFor(stepId, label) {
5838
5477
  const alias = EFFECT_ALIASES[stepId][label];
5839
5478
  if (alias !== void 0) return alias;
5840
5479
  const folded = label.toLowerCase();
5841
- return FILLABLE_STEP_VOCABULARIES[stepId].find((v) => v.toLowerCase() === folded);
5480
+ return LABEL_SUPPLY_VOCABULARIES[stepId].find((v) => v.toLowerCase() === folded);
5842
5481
  }
5843
5482
  /**
5844
- * Which steps a root detector fills, from its labelmap and classMap. A label
5845
- * counts for a step only when the model's classMap sends it to that step's
5846
- * macro AND it names a vocabulary entry. A step none of the labels reach is
5847
- * not declared; a COCO labelmap declares nothing.
5483
+ * Which steps a root detector can supply, from its labelmap and classMap.
5484
+ *
5485
+ * - label kind: a label counts for a step only when the classMap sends it to
5486
+ * that step's macro AND it names a vocabulary entry. Every root is judged
5487
+ * the same way — a COCO model supplies the animal classifier with the six
5488
+ * species COCO names, exactly as a Frigate+ model supplies seven.
5489
+ * - crop kind: the labelmap carries one of the step's root labels. Those
5490
+ * labels are deliberately left out of the classMap, so they never make a
5491
+ * track of their own.
5492
+ *
5493
+ * No classMap ⇒ no label supply (nothing says which macro a label lives under).
5848
5494
  */
5849
- function deriveClassifierFills(labels, classMap) {
5850
- if (classMap === void 0 || isCocoLabelmap(labels)) return [];
5851
- const fills = [];
5852
- for (const stepId of FILLABLE_STEP_IDS) {
5495
+ function deriveRootSupplies(labels, classMap) {
5496
+ const supplies = [];
5497
+ if (classMap !== void 0) for (const stepId of LABEL_SUPPLY_STEP_IDS) {
5853
5498
  const labelToEffect = {};
5854
5499
  for (const label of labels) {
5855
- if (classMap.mapping[label] !== FILLABLE_STEP_MACRO[stepId]) continue;
5500
+ if (classMap.mapping[label] !== LABEL_SUPPLY_MACRO[stepId]) continue;
5856
5501
  const effect = effectFor(stepId, label);
5857
5502
  if (effect !== void 0) labelToEffect[label] = effect;
5858
5503
  }
5859
5504
  const covered = new Set(Object.values(labelToEffect));
5860
5505
  if (covered.size === 0) continue;
5861
- const uncovered = FILLABLE_STEP_VOCABULARIES[stepId].filter((v) => !covered.has(v));
5862
- fills.push({
5506
+ const uncovered = LABEL_SUPPLY_VOCABULARIES[stepId].filter((v) => !covered.has(v));
5507
+ supplies.push({
5508
+ kind: "label",
5863
5509
  stepId,
5864
5510
  labelToEffect,
5865
5511
  uncovered
5866
5512
  });
5867
5513
  }
5868
- return fills;
5869
- }
5870
- /** The fill a model declares for one step, if any. */
5871
- function fillFor(entry, stepId) {
5872
- return entry?.fillsClassifierEffects?.find((f) => f.stepId === stepId);
5873
- }
5874
- /** Read the `labelLink` policy off a step's settings bag; anything else ⇒ the default. */
5875
- function readLabelLinkPolicy(settings) {
5876
- const raw = settings?.[LABEL_LINK_SETTING_KEY];
5877
- return LABEL_LINK_POLICIES.find((p) => p === raw) ?? "skip-when-filled";
5878
- }
5879
- /**
5880
- * Decide the writer of one filled effect for one camera.
5881
- *
5882
- * - The step is not running (disabled, or not in the camera's tree): the
5883
- * detector is the only writer — authoritative, nothing to skip.
5884
- * - The step runs, the policy is `skip-when-filled` and every coverage gap is
5885
- * switched off in `enabledClasses`: the detector is authoritative and the
5886
- * step is skipped.
5887
- * - Otherwise the step runs and is the writer; the linked label is a score-0
5888
- * fallback.
5889
- */
5890
- function resolveLabelLink(fill, step) {
5891
- const policy = readLabelLinkPolicy(step?.settings);
5892
- const rawEnabled = step?.settings?.["enabledClasses"];
5893
- const allowList = Array.isArray(rawEnabled) && rawEnabled.length > 0 ? rawEnabled.filter((v) => typeof v === "string") : null;
5894
- const openGaps = fill.uncovered.filter((g) => allowList === null || allowList.includes(g));
5895
- if (step === void 0 || !step.enabled) return {
5896
- authoritative: true,
5897
- skip: false,
5898
- policy,
5899
- openGaps
5900
- };
5901
- const skip = policy === "skip-when-filled" && openGaps.length === 0;
5514
+ const present = new Set(labels);
5515
+ for (const stepId of CROP_SUPPLY_STEP_IDS) {
5516
+ const source = CROP_SUPPLY_SOURCES[stepId];
5517
+ const rootLabels = source.rootLabels.filter((l) => present.has(l));
5518
+ if (rootLabels.length === 0) continue;
5519
+ supplies.push({
5520
+ kind: "crop",
5521
+ stepId,
5522
+ rootLabels,
5523
+ parentMacro: source.parentMacro,
5524
+ childClass: source.childClass
5525
+ });
5526
+ }
5527
+ return supplies;
5528
+ }
5529
+ /**
5530
+ * The supplies a root model declares. A row registered with a declaration
5531
+ * carries it (`suppliesSteps`, computed at import from the model's own
5532
+ * labelmap); a row without one — a built-in entry, or a custom row registered
5533
+ * before the field existed — is derived here, on read, from the same inputs.
5534
+ */
5535
+ function rootSuppliesOf(entry, stepDefaults) {
5536
+ const declared = entry?.suppliesSteps ?? deriveRootSupplies(entry !== void 0 && entry.labels.length > 0 ? entry.labels.map((l) => l.id) : stepDefaults.labels, entry?.classMap ?? stepDefaults.classMap);
5537
+ const inputPx = entry !== void 0 ? Math.min(entry.inputSize.width, entry.inputSize.height) : void 0;
5538
+ if (inputPx === void 0) return declared;
5539
+ return declared.map((s) => s.kind === "crop" ? {
5540
+ ...s,
5541
+ rootInputPx: inputPx
5542
+ } : s);
5543
+ }
5544
+ /** The supply a root declares for one step, if any. */
5545
+ function supplyFor(supplies, stepId) {
5546
+ return supplies?.find((s) => s.stepId === stepId);
5547
+ }
5548
+ /**
5549
+ * Read the `useRootModel` flag off a step's settings bag. Absent ⇒ the
5550
+ * retired `labelLink` value if one is stored, else the step's default.
5551
+ */
5552
+ function readUseRootModel(stepId, settings) {
5553
+ const raw = settings?.[USE_ROOT_MODEL_SETTING_KEY];
5554
+ if (typeof raw === "boolean") return raw;
5555
+ const retired = settings?.[RETIRED_LABEL_LINK_KEY];
5556
+ if (retired === "skip-when-filled") return true;
5557
+ if (retired === "run") return false;
5558
+ return USE_ROOT_MODEL_DEFAULTS[stepId];
5559
+ }
5560
+ /** Why a DECLARED supply is still not usable, or `undefined` when it is. */
5561
+ function supplyBlocker(supply) {
5562
+ if (supply.kind !== "crop") return void 0;
5563
+ const min = CROP_SUPPLY_SOURCES[supply.stepId].minRootInputPx;
5564
+ if (min === void 0) return void 0;
5565
+ return supply.rootInputPx !== void 0 && supply.rootInputPx >= min ? void 0 : "root-input-too-small";
5566
+ }
5567
+ /**
5568
+ * THE decision. Effective = the operator intends it AND the current root
5569
+ * declares it. It never looks at, changes or clears the step's `modelId`: a
5570
+ * root that cannot supply the step leaves the step running its own model, and
5571
+ * switching back to a root that can re-activates the supply by itself.
5572
+ */
5573
+ function resolveRootSupply(stepId, settings, rootSupplies) {
5574
+ const supply = supplyFor(rootSupplies, stepId);
5575
+ const intent = readUseRootModel(stepId, settings);
5576
+ const blocker = supply === void 0 ? "root-does-not-declare" : supplyBlocker(supply);
5902
5577
  return {
5903
- authoritative: skip,
5904
- skip,
5905
- policy,
5906
- openGaps
5578
+ stepId,
5579
+ supply,
5580
+ intent,
5581
+ effective: intent && blocker === void 0,
5582
+ inactive: intent && blocker !== void 0,
5583
+ ...intent && blocker !== void 0 ? { inactiveReason: blocker } : {}
5907
5584
  };
5908
5585
  }
5586
+ /**
5587
+ * The fine label a supplied root class becomes for a step whose supply is
5588
+ * effective, or `undefined` when the step's `enabledClasses` switched it off —
5589
+ * the detection then stays at its macro (`animal`). Empty / absent allow-list
5590
+ * = every class.
5591
+ */
5592
+ function allowedSuppliedEffect(effect, settings) {
5593
+ const raw = settings?.["enabledClasses"];
5594
+ if (!Array.isArray(raw) || raw.length === 0) return true;
5595
+ return raw.some((v) => v === effect);
5596
+ }
5597
+ /** The child steps of a crop-kind step that still run on the root's box. */
5598
+ function rootSupplyServes(stepId) {
5599
+ return CROP_SUPPLY_SOURCES[stepId].servedChildSteps;
5600
+ }
5601
+ /** Minimum fraction of a child box's area that must lie inside its parent. */
5602
+ var ROOT_CHILD_CONTAINMENT_MIN = .9;
5603
+ /**
5604
+ * The parent a root child box belongs to: among `parents`, the SMALLEST whose
5605
+ * box holds at least {@link ROOT_CHILD_CONTAINMENT_MIN} of the child's area
5606
+ * (a plate inside the car, not inside the bus behind it). `-1` when none does
5607
+ * — the box then belongs to nobody and makes nothing.
5608
+ */
5609
+ function containingParentIndex(child, parents) {
5610
+ const childArea = child.w * child.h;
5611
+ if (!(childArea > 0)) return -1;
5612
+ let best = -1;
5613
+ let bestArea = Number.POSITIVE_INFINITY;
5614
+ parents.forEach((p, i) => {
5615
+ if (Math.max(0, Math.min(child.x + child.w, p.x + p.w) - Math.max(child.x, p.x)) * Math.max(0, Math.min(child.y + child.h, p.y + p.h) - Math.max(child.y, p.y)) / childArea < .9) return;
5616
+ const area = p.w * p.h;
5617
+ if (area < bestArea) {
5618
+ best = i;
5619
+ bestArea = area;
5620
+ }
5621
+ });
5622
+ return best;
5623
+ }
5624
+ var SUPPLY_NOUN = {
5625
+ "animal-classifier": "animals",
5626
+ "vehicle-classifier": "vehicles",
5627
+ "package-detection": "packages",
5628
+ "plate-detection": "plates",
5629
+ "face-detection": "faces"
5630
+ };
5631
+ /** One short phrase per supply — `animals 6/8`, `plates`. */
5632
+ function describeRootSupply(supply) {
5633
+ const noun = SUPPLY_NOUN[supply.stepId];
5634
+ if (supply.kind === "crop") return noun;
5635
+ const total = LABEL_SUPPLY_VOCABULARIES[supply.stepId].length;
5636
+ if (total <= 1) return noun;
5637
+ return `${noun} ${String(total - supply.uncovered.length)}/${String(total)}`;
5638
+ }
5639
+ /** `animals 6/8 · vehicles 5/8 · plates · faces` — empty string for none. */
5640
+ function describeRootSupplies(supplies) {
5641
+ return (supplies ?? []).map(describeRootSupply).join(" · ");
5642
+ }
5909
5643
  //#endregion
5910
5644
  //#region src/types/models.ts
5911
5645
  var MODEL_FORMATS = [
@@ -6062,12 +5796,35 @@ var ModelLicenseRecordSchema = zod.z.object({
6062
5796
  * `camstack/docs` → `/docs/detection/models`.
6063
5797
  */
6064
5798
  var ModelLicenseSchema = zod.z.preprocess((value) => typeof value === "string" ? { weights: value } : value, ModelLicenseRecordSchema);
6065
- /** One `fillsClassifierEffects` entry — see `ModelCatalogEntry.fillsClassifierEffects`. */
6066
- var FillsClassifierEffectSchema = zod.z.object({
6067
- stepId: zod.z.enum(FILLABLE_STEP_IDS),
5799
+ /**
5800
+ * One step a ROOT detector can supply from its own output (D730) — see
5801
+ * `ModelCatalogEntry.suppliesSteps`.
5802
+ *
5803
+ * - `label`: the root's raw class IS the step's answer. `labelToEffect` maps
5804
+ * the root's label to the step's vocabulary; `uncovered` is the vocabulary
5805
+ * the root cannot produce (shown as unavailable, never a blocker).
5806
+ * - `crop`: the root draws the box the step would have found (`license_plate`
5807
+ * for plate-detection, `face` for face-detection). The box becomes a detail
5808
+ * child of the `parentMacro` detection that contains it — never a track.
5809
+ */
5810
+ var RootStepSupplySchema = zod.z.discriminatedUnion("kind", [zod.z.object({
5811
+ kind: zod.z.literal("label"),
5812
+ stepId: zod.z.enum(LABEL_SUPPLY_STEP_IDS),
6068
5813
  labelToEffect: zod.z.record(zod.z.string(), zod.z.string()),
6069
5814
  uncovered: zod.z.array(zod.z.string()).readonly()
6070
- });
5815
+ }), zod.z.object({
5816
+ kind: zod.z.literal("crop"),
5817
+ stepId: zod.z.enum(CROP_SUPPLY_STEP_IDS),
5818
+ rootLabels: zod.z.array(zod.z.string()).readonly(),
5819
+ parentMacro: zod.z.enum(["vehicle", "person"]),
5820
+ childClass: zod.z.enum(["plate", "face"]),
5821
+ /**
5822
+ * The root's smaller input side, stamped on read by `rootSuppliesOf` from
5823
+ * the model entry — what a step's `minRootInputPx` gate judges. Never
5824
+ * stored by an import.
5825
+ */
5826
+ rootInputPx: zod.z.number().int().positive().optional()
5827
+ })]);
6071
5828
  var ModelCatalogEntrySchema = zod.z.object({
6072
5829
  id: zod.z.string(),
6073
5830
  name: zod.z.string(),
@@ -6194,15 +5951,15 @@ var ModelCatalogEntrySchema = zod.z.object({
6194
5951
  */
6195
5952
  classMap: DetectionCatalogClassMapSchema.optional(),
6196
5953
  /**
6197
- * The pipeline steps this ROOT detector already does the work of (D715):
6198
- * its labels name the effect the step exists to produce (`deer` for
6199
- * `animal-classifier`, `package` for `package-detection`). Derived from the
6200
- * labelmap by `deriveClassifierFills`, never hand-written; a COCO model
6201
- * declares none. Whether a declaration is honoured is the step's own
6202
- * `labelLink` policy (`resolveLabelLink`). `labelToEffect` values are in
6203
- * `FILLABLE_STEP_VOCABULARIES` (guard `check-label-link-vocabulary`).
5954
+ * The pipeline steps this ROOT detector can supply from its own output
5955
+ * (D730): its labels name a step's effect (`deer` for `animal-classifier`)
5956
+ * or draw a step's box (`license_plate` for `plate-detection`). Computed at
5957
+ * import from THIS model's labelmap by `deriveRootSupplies`, never written
5958
+ * by hand and never assumed per family; a row without it is derived on read
5959
+ * (`rootSuppliesOf`). Whether a camera uses it is the step's own
5960
+ * `useRootModel` flag (`resolveRootSupply`).
6204
5961
  */
6205
- fillsClassifierEffects: zod.z.array(FillsClassifierEffectSchema).readonly().optional(),
5962
+ suppliesSteps: zod.z.array(RootStepSupplySchema).readonly().optional(),
6206
5963
  /**
6207
5964
  * The model's CLIP feature-space contract (text encoder, dimension, vector
6208
5965
  * index, token window, search floor) — see `clip-model.ts`. Present ONLY on a
@@ -10995,7 +10752,7 @@ function unavailableFor(tf, role, mode, cap, at) {
10995
10752
  };
10996
10753
  return {
10997
10754
  ok: false,
10998
- problem: problem("not-replaceable", at, `\`${cap}.${tf.path}\` reads a source but can say neither null nor 'unknown', and an ADDED capability has no native reading to fall back to (D393, D663)`)
10755
+ problem: problem("not-replaceable", at, `\`${cap}.${tf.path}\` reads a source but can say neither null nor 'unknown', and an ADDED capability has no native reading to fall back to`)
10999
10756
  };
11000
10757
  }
11001
10758
  //#endregion
@@ -16359,6 +16116,368 @@ var filesystemBrowseCapability = {
16359
16116
  }
16360
16117
  };
16361
16118
  //#endregion
16119
+ //#region src/capabilities/viewer-link-url.ts
16120
+ /** The central link domain the viewer app claims (Universal Links / App Links). */
16121
+ var CAMSTACK_APP_LINK_ORIGIN = "https://camstack.zentik.app";
16122
+ /** Where the hub serves the viewer SPA. */
16123
+ var HUB_VIEWER_MOUNT = "/viewer/camstack";
16124
+ /** True when `baseUrl` names the CamStack app link rather than a hub address. */
16125
+ function isAppLinkBase(baseUrl) {
16126
+ if (baseUrl === null || baseUrl === void 0 || baseUrl === "") return false;
16127
+ try {
16128
+ return new URL(baseUrl).origin === CAMSTACK_APP_LINK_ORIGIN;
16129
+ } catch {
16130
+ return false;
16131
+ }
16132
+ }
16133
+ /**
16134
+ * The base a viewer link is built on, from the flagged notification address:
16135
+ * the effective address is the explicit choice, else AUTO's `resolved`. The
16136
+ * one reading of that answer — the NC's tap and the Network page's preview
16137
+ * both go through it.
16138
+ */
16139
+ function viewerLinkBaseOf(endpoint) {
16140
+ const flagged = endpoint.baseUrl ?? endpoint.resolved;
16141
+ return isAppLinkBase(flagged) ? {
16142
+ form: "app",
16143
+ hubBaseUrl: endpoint.appLinkHub ?? null
16144
+ } : {
16145
+ form: "hub",
16146
+ hubBaseUrl: flagged
16147
+ };
16148
+ }
16149
+ /**
16150
+ * Reduce a base URL to the bare origin the central page accepts as `hub=`,
16151
+ * or `null` when it cannot be one: not http(s), carries credentials, or does
16152
+ * not parse. A path/query/fragment is dropped — the central page mounts the
16153
+ * viewer under the origin itself.
16154
+ */
16155
+ function toAppLinkHubOrigin(baseUrl) {
16156
+ let parsed;
16157
+ try {
16158
+ parsed = new URL(baseUrl);
16159
+ } catch {
16160
+ return null;
16161
+ }
16162
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
16163
+ if (parsed.username !== "" || parsed.password !== "") return null;
16164
+ if (parsed.hostname === "") return null;
16165
+ return parsed.origin;
16166
+ }
16167
+ /**
16168
+ * The hub address an app link carries as `hub=`, chosen from the hub's OWN
16169
+ * advertised palette so it always agrees with what the Network page lists:
16170
+ * the public tunnel first, then the mesh (Tailscale MagicDNS) — HTTPS only,
16171
+ * because the app and the hosted viewer reach it from off the LAN. `null`
16172
+ * when the hub advertises neither.
16173
+ */
16174
+ function pickAppLinkHub(endpoints) {
16175
+ const rank = (e) => {
16176
+ if (e.kind !== "public" || !e.baseUrl.startsWith("https://")) return null;
16177
+ if (e.interfaceKind === "public") return 0;
16178
+ if (e.interfaceKind === "vpn") return 1;
16179
+ return null;
16180
+ };
16181
+ return endpoints.flatMap((e) => {
16182
+ const r = rank(e);
16183
+ const origin = toAppLinkHubOrigin(e.baseUrl);
16184
+ return r === null || origin === null ? [] : [{
16185
+ r,
16186
+ p: e.priority,
16187
+ origin
16188
+ }];
16189
+ }).toSorted((a, b) => a.r - b.r || a.p - b.p)[0]?.origin ?? null;
16190
+ }
16191
+ /** The viewer route for a destination, or null when it names nothing. */
16192
+ function viewerRoute(destination) {
16193
+ switch (destination.kind) {
16194
+ case "camera": {
16195
+ if (!Number.isInteger(destination.deviceId) || destination.deviceId <= 0) return null;
16196
+ const at = destination.atMs;
16197
+ return {
16198
+ path: `/camera/${String(destination.deviceId)}`,
16199
+ query: at !== void 0 && Number.isFinite(at) ? [["at", String(Math.trunc(at))]] : []
16200
+ };
16201
+ }
16202
+ case "events": return {
16203
+ path: "/events",
16204
+ query: []
16205
+ };
16206
+ case "notifications": return {
16207
+ path: "/notifications",
16208
+ query: []
16209
+ };
16210
+ }
16211
+ }
16212
+ function encodeQuery(query) {
16213
+ if (query.length === 0) return "";
16214
+ return `?${query.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&")}`;
16215
+ }
16216
+ /**
16217
+ * The link for a viewer destination, or `null` when none can be minted: an
16218
+ * unknown destination, or the `hub` form with no reachable base (a dead link
16219
+ * is worse than none).
16220
+ */
16221
+ function buildViewerLink(input) {
16222
+ const route = viewerRoute(input.destination);
16223
+ if (route === null) return null;
16224
+ const raw = input.base.hubBaseUrl;
16225
+ const base = raw === null ? "" : raw.replace(/\/+$/, "");
16226
+ if (input.base.form === "app") {
16227
+ const origin = base === "" ? null : toAppLinkHubOrigin(base);
16228
+ const query = origin === null ? route.query : [...route.query, ["hub", origin]];
16229
+ return `${CAMSTACK_APP_LINK_ORIGIN}${route.path}${encodeQuery(query)}`;
16230
+ }
16231
+ if (base === "" || isAppLinkBase(base)) return null;
16232
+ return `${base}${HUB_VIEWER_MOUNT}${route.path}${encodeQuery(route.query)}`;
16233
+ }
16234
+ //#endregion
16235
+ //#region src/capabilities/hub-address.ts
16236
+ var HUB_ADDRESS_ROLES = [
16237
+ "viewer",
16238
+ "notifications",
16239
+ "agents",
16240
+ "signIn"
16241
+ ];
16242
+ var SINGLE_HUB_ADDRESS_ROLES = [
16243
+ "notifications",
16244
+ "agents",
16245
+ "signIn"
16246
+ ];
16247
+ function isSingleHubAddressRole(role) {
16248
+ return role !== "viewer";
16249
+ }
16250
+ var AUTOMATIC_HUB_ADDRESS_FLAGS = {
16251
+ viewer: [],
16252
+ notifications: null,
16253
+ agents: null,
16254
+ signIn: null
16255
+ };
16256
+ var IPV4_LITERAL = /^\d{1,3}(?:\.\d{1,3}){3}$/;
16257
+ function hostnameOf(baseUrl) {
16258
+ try {
16259
+ const host = new URL(baseUrl).hostname;
16260
+ return host.length > 0 ? host : null;
16261
+ } catch {
16262
+ return null;
16263
+ }
16264
+ }
16265
+ function trimSlash(baseUrl) {
16266
+ return baseUrl.replace(/\/+$/, "");
16267
+ }
16268
+ function isMesh(endpoint) {
16269
+ return endpoint.kind === "public" && endpoint.interfaceKind === "vpn";
16270
+ }
16271
+ function isTunnel(endpoint) {
16272
+ return endpoint.kind === "public" && endpoint.interfaceKind === "public";
16273
+ }
16274
+ function isPlausibleLan(endpoint) {
16275
+ return (endpoint.kind === "lan-ipv4" || endpoint.kind === "lan-ipv6") && endpoint.plausible;
16276
+ }
16277
+ function isLanIpv4Literal(endpoint) {
16278
+ if (endpoint.kind !== "lan-ipv4" || !endpoint.plausible) return false;
16279
+ const host = hostnameOf(endpoint.baseUrl);
16280
+ return host !== null && IPV4_LITERAL.test(host);
16281
+ }
16282
+ /** Lower = better; `null` = never this role's AUTOMATIC answer. */
16283
+ function autoRankFor(role, endpoint) {
16284
+ if (endpoint.kind === "loopback" || endpoint.kind === "app-link" || endpoint.kind === "custom") return null;
16285
+ if (hostnameOf(endpoint.baseUrl) === null) return null;
16286
+ if (role === "agents") {
16287
+ if (isLanIpv4Literal(endpoint)) return 0;
16288
+ if (isPlausibleLan(endpoint)) return 1;
16289
+ if (isMesh(endpoint)) return 2;
16290
+ return null;
16291
+ }
16292
+ if (isTunnel(endpoint)) return 0;
16293
+ if (isMesh(endpoint)) return 1;
16294
+ if (isLanIpv4Literal(endpoint)) return 2;
16295
+ if (isPlausibleLan(endpoint)) return 3;
16296
+ return null;
16297
+ }
16298
+ var LOOPBACK_HOSTS = new Set([
16299
+ "localhost",
16300
+ "0.0.0.0",
16301
+ "::1",
16302
+ "[::1]",
16303
+ "::",
16304
+ "[::]"
16305
+ ]);
16306
+ function isLoopbackHost(hostname) {
16307
+ const h = hostname.toLowerCase();
16308
+ if (LOOPBACK_HOSTS.has(h) || h.endsWith(".localhost")) return true;
16309
+ return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(h);
16310
+ }
16311
+ /**
16312
+ * Validate an operator-typed address: an http(s) URL with a host, no
16313
+ * credentials, no path, and never loopback (it would name whoever dials it).
16314
+ * Answers the normalised origin (`scheme://host[:port]`).
16315
+ */
16316
+ function validateCustomAddress(raw) {
16317
+ const trimmed = raw.trim();
16318
+ if (trimmed === "") return {
16319
+ ok: false,
16320
+ reason: "Enter an address, e.g. https://192.168.1.50:4443."
16321
+ };
16322
+ let parsed;
16323
+ try {
16324
+ parsed = new URL(trimmed);
16325
+ } catch {
16326
+ return {
16327
+ ok: false,
16328
+ reason: "Not a URL — include http:// or https://."
16329
+ };
16330
+ }
16331
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return {
16332
+ ok: false,
16333
+ reason: "Use an http:// or https:// address."
16334
+ };
16335
+ if (parsed.username !== "" || parsed.password !== "") return {
16336
+ ok: false,
16337
+ reason: "The address must not carry a user name or password."
16338
+ };
16339
+ if (parsed.hostname === "") return {
16340
+ ok: false,
16341
+ reason: "The address has no host."
16342
+ };
16343
+ if (isLoopbackHost(parsed.hostname)) return {
16344
+ ok: false,
16345
+ reason: "A loopback address names whoever dials it, never this hub — use an address other machines reach."
16346
+ };
16347
+ if (parsed.pathname !== "/" || parsed.search !== "" || parsed.hash !== "") return {
16348
+ ok: false,
16349
+ reason: "Enter only the address (scheme, host and port), without a path."
16350
+ };
16351
+ return {
16352
+ ok: true,
16353
+ baseUrl: parsed.origin
16354
+ };
16355
+ }
16356
+ /** The reason a plain-http sign-in address is refused unless accepted. */
16357
+ var SIGN_IN_HTTP_REASON = "Sign-in needs an https:// address: passkeys and most identity providers refuse a plain-http origin. Accept http only for a sign-in that never leaves your own network.";
16358
+ /**
16359
+ * May `baseUrl` carry `role`? The address check above, plus two role rules:
16360
+ * the app link carries `notifications` only (it is not a hub address), and a
16361
+ * plain-http address carries `signIn` only when the operator explicitly
16362
+ * accepted it (`acceptInsecureHttp`) — a LAN-only password or magic-link
16363
+ * sign-in works over http, a passkey or an OIDC callback does not.
16364
+ */
16365
+ function checkRoleAddress(role, baseUrl, options = {}) {
16366
+ if (isAppLinkBase(baseUrl)) return role === "notifications" ? {
16367
+ ok: true,
16368
+ baseUrl: CAMSTACK_APP_LINK_ORIGIN
16369
+ } : {
16370
+ ok: false,
16371
+ reason: "The CamStack app link is not a hub address — only notifications can use it."
16372
+ };
16373
+ const check = validateCustomAddress(baseUrl);
16374
+ if (!check.ok) return check;
16375
+ if (role === "signIn" && check.baseUrl.startsWith("http:") && options.acceptInsecureHttp !== true) return {
16376
+ ok: false,
16377
+ reason: SIGN_IN_HTTP_REASON
16378
+ };
16379
+ return check;
16380
+ }
16381
+ /** A stored flag that still validates (stored values are external data). */
16382
+ function validFlag(role, flag) {
16383
+ if (flag === null || flag === "") return null;
16384
+ const check = checkRoleAddress(role, flag, { acceptInsecureHttp: true });
16385
+ return check.ok ? check.baseUrl : null;
16386
+ }
16387
+ function pickAuto(endpoints, role) {
16388
+ const best = endpoints.flatMap((endpoint) => {
16389
+ const rank = autoRankFor(role, endpoint);
16390
+ return rank === null ? [] : [{
16391
+ rank,
16392
+ priority: endpoint.priority,
16393
+ endpoint
16394
+ }];
16395
+ }).toSorted((a, b) => a.rank - b.rank || a.priority - b.priority)[0];
16396
+ return best === void 0 ? null : trimSlash(best.endpoint.baseUrl);
16397
+ }
16398
+ function resolveHubRole(endpoints, role, flag) {
16399
+ const flagged = validFlag(role, flag);
16400
+ if (flagged !== null) return {
16401
+ flagged,
16402
+ resolved: flagged,
16403
+ source: "flagged"
16404
+ };
16405
+ const auto = pickAuto(endpoints, role);
16406
+ return {
16407
+ flagged: null,
16408
+ resolved: auto,
16409
+ source: auto === null ? "none" : "auto"
16410
+ };
16411
+ }
16412
+ /** `agents`: what agents and LAN devices pull from. */
16413
+ function resolveAgentsAddress(endpoints, flags) {
16414
+ return resolveHubRole(endpoints, "agents", flags.agents);
16415
+ }
16416
+ /** `signIn`: sign-in links, OIDC callbacks, the relying party, OAuth2. */
16417
+ function resolveSignInAddress(endpoints, flags) {
16418
+ return resolveHubRole(endpoints, "signIn", flags.signIn);
16419
+ }
16420
+ /** `notifications`: the flagged address, else the CamStack app link (D709). */
16421
+ function resolveNotificationsAddress(flags) {
16422
+ const flagged = validFlag("notifications", flags.notifications);
16423
+ if (flagged !== null && !isAppLinkBase(flagged)) return {
16424
+ flagged,
16425
+ resolved: flagged,
16426
+ source: "flagged"
16427
+ };
16428
+ return {
16429
+ flagged: null,
16430
+ resolved: CAMSTACK_APP_LINK_ORIGIN,
16431
+ source: "auto"
16432
+ };
16433
+ }
16434
+ /** AUTO viewer race: LAN IPv4 + the public tunnel. Never IPv6, mesh, loopback or custom. */
16435
+ function isDefaultViewerEndpoint(endpoint) {
16436
+ if (endpoint.kind === "lan-ipv4") return true;
16437
+ return endpoint.kind === "public" && endpoint.interfaceKind === "public";
16438
+ }
16439
+ /**
16440
+ * The endpoints the apps race: the flagged ones that are in `endpoints`
16441
+ * (which carries the custom addresses flagged `viewer`), else AUTO. A flagged
16442
+ * set none of which is currently up falls back to AUTO rather than racing
16443
+ * nothing.
16444
+ */
16445
+ function selectViewerEndpoints(endpoints, flags) {
16446
+ const auto = endpoints.filter(isDefaultViewerEndpoint);
16447
+ if (flags.viewer.length === 0) return auto;
16448
+ const wanted = new Set(flags.viewer);
16449
+ const explicit = endpoints.filter((e) => wanted.has(e.baseUrl));
16450
+ return explicit.length > 0 ? explicit : auto;
16451
+ }
16452
+ /** `viewer`: what the apps race and connect to. */
16453
+ function resolveViewerAddresses(endpoints, flags) {
16454
+ const wanted = new Set(flags.viewer);
16455
+ const selected = selectViewerEndpoints(endpoints, flags);
16456
+ const usedFlags = selected.some((e) => wanted.has(e.baseUrl));
16457
+ return {
16458
+ flagged: flags.viewer,
16459
+ resolved: selected.map((e) => e.baseUrl),
16460
+ source: selected.length === 0 ? "none" : usedFlags ? "flagged" : "auto"
16461
+ };
16462
+ }
16463
+ /**
16464
+ * Every role at once, each through its own resolver. `viewerPalette` is what
16465
+ * the apps race (it carries the custom addresses flagged `viewer`);
16466
+ * `hubPalette` is the detected HTTPS palette agents and sign-in resolve over.
16467
+ */
16468
+ function resolveHubAddressRoles(viewerPalette, hubPalette, flags) {
16469
+ return {
16470
+ viewer: resolveViewerAddresses(viewerPalette, flags),
16471
+ notifications: resolveNotificationsAddress(flags),
16472
+ agents: resolveAgentsAddress(hubPalette, flags),
16473
+ signIn: resolveSignInAddress(hubPalette, flags)
16474
+ };
16475
+ }
16476
+ /** The bare hostname of a base URL, or `null` when it does not parse. */
16477
+ function hubAddressHostname(baseUrl) {
16478
+ return baseUrl === null ? null : hostnameOf(baseUrl);
16479
+ }
16480
+ //#endregion
16362
16481
  //#region src/capabilities/ai-cap-timeouts.ts
16363
16482
  /**
16364
16483
  * Transport bounds for AI capability methods.
@@ -18591,6 +18710,398 @@ var notificationOutputCapability = {
18591
18710
  }
18592
18711
  };
18593
18712
  //#endregion
18713
+ //#region src/catalogs/coco-classmap.ts
18714
+ var COCO_80_LABELS = [
18715
+ {
18716
+ id: "person",
18717
+ name: "Person"
18718
+ },
18719
+ {
18720
+ id: "bicycle",
18721
+ name: "Bicycle"
18722
+ },
18723
+ {
18724
+ id: "car",
18725
+ name: "Car"
18726
+ },
18727
+ {
18728
+ id: "motorcycle",
18729
+ name: "Motorcycle"
18730
+ },
18731
+ {
18732
+ id: "airplane",
18733
+ name: "Airplane"
18734
+ },
18735
+ {
18736
+ id: "bus",
18737
+ name: "Bus"
18738
+ },
18739
+ {
18740
+ id: "train",
18741
+ name: "Train"
18742
+ },
18743
+ {
18744
+ id: "truck",
18745
+ name: "Truck"
18746
+ },
18747
+ {
18748
+ id: "boat",
18749
+ name: "Boat"
18750
+ },
18751
+ {
18752
+ id: "traffic light",
18753
+ name: "Traffic Light"
18754
+ },
18755
+ {
18756
+ id: "fire hydrant",
18757
+ name: "Fire Hydrant"
18758
+ },
18759
+ {
18760
+ id: "stop sign",
18761
+ name: "Stop Sign"
18762
+ },
18763
+ {
18764
+ id: "parking meter",
18765
+ name: "Parking Meter"
18766
+ },
18767
+ {
18768
+ id: "bench",
18769
+ name: "Bench"
18770
+ },
18771
+ {
18772
+ id: "bird",
18773
+ name: "Bird"
18774
+ },
18775
+ {
18776
+ id: "cat",
18777
+ name: "Cat"
18778
+ },
18779
+ {
18780
+ id: "dog",
18781
+ name: "Dog"
18782
+ },
18783
+ {
18784
+ id: "horse",
18785
+ name: "Horse"
18786
+ },
18787
+ {
18788
+ id: "sheep",
18789
+ name: "Sheep"
18790
+ },
18791
+ {
18792
+ id: "cow",
18793
+ name: "Cow"
18794
+ },
18795
+ {
18796
+ id: "elephant",
18797
+ name: "Elephant"
18798
+ },
18799
+ {
18800
+ id: "bear",
18801
+ name: "Bear"
18802
+ },
18803
+ {
18804
+ id: "zebra",
18805
+ name: "Zebra"
18806
+ },
18807
+ {
18808
+ id: "giraffe",
18809
+ name: "Giraffe"
18810
+ },
18811
+ {
18812
+ id: "backpack",
18813
+ name: "Backpack"
18814
+ },
18815
+ {
18816
+ id: "umbrella",
18817
+ name: "Umbrella"
18818
+ },
18819
+ {
18820
+ id: "handbag",
18821
+ name: "Handbag"
18822
+ },
18823
+ {
18824
+ id: "tie",
18825
+ name: "Tie"
18826
+ },
18827
+ {
18828
+ id: "suitcase",
18829
+ name: "Suitcase"
18830
+ },
18831
+ {
18832
+ id: "frisbee",
18833
+ name: "Frisbee"
18834
+ },
18835
+ {
18836
+ id: "skis",
18837
+ name: "Skis"
18838
+ },
18839
+ {
18840
+ id: "snowboard",
18841
+ name: "Snowboard"
18842
+ },
18843
+ {
18844
+ id: "sports ball",
18845
+ name: "Sports Ball"
18846
+ },
18847
+ {
18848
+ id: "kite",
18849
+ name: "Kite"
18850
+ },
18851
+ {
18852
+ id: "baseball bat",
18853
+ name: "Baseball Bat"
18854
+ },
18855
+ {
18856
+ id: "baseball glove",
18857
+ name: "Baseball Glove"
18858
+ },
18859
+ {
18860
+ id: "skateboard",
18861
+ name: "Skateboard"
18862
+ },
18863
+ {
18864
+ id: "surfboard",
18865
+ name: "Surfboard"
18866
+ },
18867
+ {
18868
+ id: "tennis racket",
18869
+ name: "Tennis Racket"
18870
+ },
18871
+ {
18872
+ id: "bottle",
18873
+ name: "Bottle"
18874
+ },
18875
+ {
18876
+ id: "wine glass",
18877
+ name: "Wine Glass"
18878
+ },
18879
+ {
18880
+ id: "cup",
18881
+ name: "Cup"
18882
+ },
18883
+ {
18884
+ id: "fork",
18885
+ name: "Fork"
18886
+ },
18887
+ {
18888
+ id: "knife",
18889
+ name: "Knife"
18890
+ },
18891
+ {
18892
+ id: "spoon",
18893
+ name: "Spoon"
18894
+ },
18895
+ {
18896
+ id: "bowl",
18897
+ name: "Bowl"
18898
+ },
18899
+ {
18900
+ id: "banana",
18901
+ name: "Banana"
18902
+ },
18903
+ {
18904
+ id: "apple",
18905
+ name: "Apple"
18906
+ },
18907
+ {
18908
+ id: "sandwich",
18909
+ name: "Sandwich"
18910
+ },
18911
+ {
18912
+ id: "orange",
18913
+ name: "Orange"
18914
+ },
18915
+ {
18916
+ id: "broccoli",
18917
+ name: "Broccoli"
18918
+ },
18919
+ {
18920
+ id: "carrot",
18921
+ name: "Carrot"
18922
+ },
18923
+ {
18924
+ id: "hot dog",
18925
+ name: "Hot Dog"
18926
+ },
18927
+ {
18928
+ id: "pizza",
18929
+ name: "Pizza"
18930
+ },
18931
+ {
18932
+ id: "donut",
18933
+ name: "Donut"
18934
+ },
18935
+ {
18936
+ id: "cake",
18937
+ name: "Cake"
18938
+ },
18939
+ {
18940
+ id: "chair",
18941
+ name: "Chair"
18942
+ },
18943
+ {
18944
+ id: "couch",
18945
+ name: "Couch"
18946
+ },
18947
+ {
18948
+ id: "potted plant",
18949
+ name: "Potted Plant"
18950
+ },
18951
+ {
18952
+ id: "bed",
18953
+ name: "Bed"
18954
+ },
18955
+ {
18956
+ id: "dining table",
18957
+ name: "Dining Table"
18958
+ },
18959
+ {
18960
+ id: "toilet",
18961
+ name: "Toilet"
18962
+ },
18963
+ {
18964
+ id: "tv",
18965
+ name: "TV"
18966
+ },
18967
+ {
18968
+ id: "laptop",
18969
+ name: "Laptop"
18970
+ },
18971
+ {
18972
+ id: "mouse",
18973
+ name: "Mouse"
18974
+ },
18975
+ {
18976
+ id: "remote",
18977
+ name: "Remote"
18978
+ },
18979
+ {
18980
+ id: "keyboard",
18981
+ name: "Keyboard"
18982
+ },
18983
+ {
18984
+ id: "cell phone",
18985
+ name: "Cell Phone"
18986
+ },
18987
+ {
18988
+ id: "microwave",
18989
+ name: "Microwave"
18990
+ },
18991
+ {
18992
+ id: "oven",
18993
+ name: "Oven"
18994
+ },
18995
+ {
18996
+ id: "toaster",
18997
+ name: "Toaster"
18998
+ },
18999
+ {
19000
+ id: "sink",
19001
+ name: "Sink"
19002
+ },
19003
+ {
19004
+ id: "refrigerator",
19005
+ name: "Refrigerator"
19006
+ },
19007
+ {
19008
+ id: "book",
19009
+ name: "Book"
19010
+ },
19011
+ {
19012
+ id: "clock",
19013
+ name: "Clock"
19014
+ },
19015
+ {
19016
+ id: "vase",
19017
+ name: "Vase"
19018
+ },
19019
+ {
19020
+ id: "scissors",
19021
+ name: "Scissors"
19022
+ },
19023
+ {
19024
+ id: "teddy bear",
19025
+ name: "Teddy Bear"
19026
+ },
19027
+ {
19028
+ id: "hair drier",
19029
+ name: "Hair Drier"
19030
+ },
19031
+ {
19032
+ id: "toothbrush",
19033
+ name: "Toothbrush"
19034
+ }
19035
+ ];
19036
+ var MACRO_LABELS = [
19037
+ {
19038
+ id: "person",
19039
+ name: "Person"
19040
+ },
19041
+ {
19042
+ id: "vehicle",
19043
+ name: "Vehicle"
19044
+ },
19045
+ {
19046
+ id: "animal",
19047
+ name: "Animal"
19048
+ },
19049
+ {
19050
+ id: "package",
19051
+ name: "Package"
19052
+ }
19053
+ ];
19054
+ /**
19055
+ * The class names a per-class COUNT can ever be keyed on — the macro ids, and
19056
+ * only those.
19057
+ *
19058
+ * `preserveOriginal: false` below is the whole reason this set exists: the
19059
+ * executor maps each raw COCO label onto its macro and DROPS everything else,
19060
+ * so a tracked detection's `className` is always one of {@link MACRO_LABELS}.
19061
+ * Any surface that indexes counts by class name — the ZoneAnalytics occupancy
19062
+ * snapshot's `byClass`, and therefore every occupancy rule — can match nothing
19063
+ * else. Derived from the catalog, never hand-listed, so a new macro joins it by
19064
+ * being declared once.
19065
+ *
19066
+ * Cost of not having it, 2026-08-12: the notification rule editor offered the
19067
+ * full taxonomy for an occupancy class (occupancy is the one condition that
19068
+ * does not go through the engine's macro-expanding `expandClassSelector`), two
19069
+ * live rules were authored on `car`, and both counters read 0 forever.
19070
+ */
19071
+ var DETECTION_MACRO_CLASSES = new Set(MACRO_LABELS.map((l) => l.id));
19072
+ /** True when `className` is a macro the detector can actually emit (and hence a
19073
+ * class a count can be keyed on) — see {@link DETECTION_MACRO_CLASSES}. */
19074
+ function isDetectionMacroClass(className) {
19075
+ return DETECTION_MACRO_CLASSES.has(className.trim().toLowerCase());
19076
+ }
19077
+ var COCO_TO_MACRO = {
19078
+ mapping: {
19079
+ person: "person",
19080
+ bicycle: "vehicle",
19081
+ car: "vehicle",
19082
+ motorcycle: "vehicle",
19083
+ airplane: "vehicle",
19084
+ bus: "vehicle",
19085
+ train: "vehicle",
19086
+ truck: "vehicle",
19087
+ boat: "vehicle",
19088
+ bird: "animal",
19089
+ cat: "animal",
19090
+ dog: "animal",
19091
+ horse: "animal",
19092
+ sheep: "animal",
19093
+ cow: "animal",
19094
+ elephant: "animal",
19095
+ bear: "animal",
19096
+ zebra: "animal",
19097
+ giraffe: "animal",
19098
+ suitcase: "package",
19099
+ backpack: "package",
19100
+ handbag: "package"
19101
+ },
19102
+ preserveOriginal: false
19103
+ };
19104
+ //#endregion
18594
19105
  //#region src/catalogs/audio-classmap.ts
18595
19106
  var AUDIO_MACRO_LABELS = [
18596
19107
  {
@@ -21248,7 +21759,7 @@ var NC_CONDITION_CATALOG = [
21248
21759
  operator: "in",
21249
21760
  appliesTo: ["immediate", "track-end"],
21250
21761
  phase: "P1",
21251
- description: "Only recognised people. Selecting NOBODY means every identity in the face gallery; selecting some narrows to those. Matched on the gallery id, so renaming a person does not break the rule. On Immediate delivery the name must already be known when an event is written — choose Immediate, grouped to wait for it (D692)."
21762
+ description: "Only recognised people. Selecting NOBODY means every identity in the face gallery; selecting some narrows to those. Matched on the gallery id, so renaming a person does not break the rule. On Immediate delivery the name must already be known when an event is written — choose Immediate, grouped to wait for it."
21252
21763
  },
21253
21764
  {
21254
21765
  id: "plates",
@@ -25848,6 +26359,34 @@ var pipelineAnalyticsCapability = {
25848
26359
  //#endregion
25849
26360
  //#region src/capabilities/pipeline-executor.cap.ts
25850
26361
  /**
26362
+ * A root model's boxes standing in for one crop-kind step's own inference
26363
+ * (D730) — see `runPipeline.suppliedRoot`.
26364
+ */
26365
+ var SuppliedRootOutputSchema = zod.z.object({
26366
+ stepId: zod.z.string(),
26367
+ /** The root model that drew the boxes — stamped on the result. */
26368
+ modelId: zod.z.string(),
26369
+ detections: zod.z.array(zod.z.object({
26370
+ /** `[x1, y1, x2, y2]` in the pixels of the call's image. */
26371
+ bbox: zod.z.tuple([
26372
+ zod.z.number(),
26373
+ zod.z.number(),
26374
+ zod.z.number(),
26375
+ zod.z.number()
26376
+ ]),
26377
+ score: zod.z.number(),
26378
+ /**
26379
+ * Five facial landmarks in the same pixels — a face the step's own
26380
+ * detector already re-found inside a root face box (D730), so its
26381
+ * embedder child aligns it exactly as it aligns any detected face.
26382
+ */
26383
+ landmarks: zod.z.array(zod.z.object({
26384
+ x: zod.z.number(),
26385
+ y: zod.z.number()
26386
+ })).optional()
26387
+ })).min(1)
26388
+ });
26389
+ /**
25851
26390
  * Reference to the frame's retained NATIVE surface + the parent crop's placement
25852
26391
  * within the frame, so the executor can re-cut a leaf child ROI at native
25853
26392
  * resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
@@ -25979,7 +26518,7 @@ var PipelineModelOptionSchema = zod.z.object({
25979
26518
  legacy: zod.z.boolean().optional(),
25980
26519
  provider: ModelProviderIdSchema.optional(),
25981
26520
  defaultConfidence: zod.z.number().optional(),
25982
- fillsClassifierEffects: zod.z.array(FillsClassifierEffectSchema).readonly().optional()
26521
+ suppliesSteps: zod.z.array(RootStepSupplySchema).readonly().optional()
25983
26522
  });
25984
26523
  var ConfigFieldBridge = zod.z.custom();
25985
26524
  var PipelineAddonSchemaSchema = zod.z.object({
@@ -26383,7 +26922,16 @@ var pipelineExecutorCapability = {
26383
26922
  * `device-not-servable`), and nothing reaches the camera's live
26384
26923
  * trace stream. Absent ⇒ unchanged behaviour.
26385
26924
  */
26386
- replay: zod.z.literal(true).optional()
26925
+ replay: zod.z.literal(true).optional(),
26926
+ /**
26927
+ * Root-supplied detections for the tree's ROOT step (D730). Set by
26928
+ * `runDetailSubtree` when the camera's root model supplies a crop-kind
26929
+ * step (a Frigate+ `license_plate` for `plate-detection`): the step is
26930
+ * NOT inferred, these boxes are its output, and its children (the plate
26931
+ * reader) run on them through their own crop policy. Boxes are in the
26932
+ * PIXELS of the image this call carries. Absent ⇒ the root step runs.
26933
+ */
26934
+ suppliedRoot: SuppliedRootOutputSchema.optional()
26387
26935
  }), PipelineRunResultBridge, { kind: "mutation" }),
26388
26936
  /**
26389
26937
  * Batched run — N raw frames packed into one cap call. The provider
@@ -27896,7 +28444,22 @@ var pipelineRunnerCapability = {
27896
28444
  */
27897
28445
  cropJpeg: zod.z.string().optional(),
27898
28446
  parent: DetailParentSchema,
27899
- steps: zod.z.array(zod.z.string()).optional()
28447
+ steps: zod.z.array(zod.z.string()).optional(),
28448
+ /**
28449
+ * The camera's root model already drew this step's boxes (D730): a
28450
+ * `license_plate` inside the parent vehicle. The named step is NOT
28451
+ * inferred — these FRAME-space boxes are its output — and its
28452
+ * requested children (the plate reader) run on them. Absent ⇒ every
28453
+ * requested step infers as before.
28454
+ */
28455
+ supplied: zod.z.object({
28456
+ stepId: zod.z.string(),
28457
+ modelId: zod.z.string(),
28458
+ boxes: zod.z.array(zod.z.object({
28459
+ bbox: NativeCropBboxSchema,
28460
+ score: zod.z.number()
28461
+ })).min(1)
28462
+ }).optional()
27900
28463
  }), zod.z.object({ details: zod.z.array(DetailResultSchema) }).nullable(), { kind: "mutation" }),
27901
28464
  /**
27902
28465
  * Run ONE enrichment step against caller-supplied pixels, with NO camera
@@ -32450,122 +33013,6 @@ var CLIP_PLAYBACK_OPTIONS = {
32450
33013
  }
32451
33014
  };
32452
33015
  //#endregion
32453
- //#region src/capabilities/viewer-link-url.ts
32454
- /** The central link domain the viewer app claims (Universal Links / App Links). */
32455
- var CAMSTACK_APP_LINK_ORIGIN = "https://camstack.zentik.app";
32456
- /** Where the hub serves the viewer SPA. */
32457
- var HUB_VIEWER_MOUNT = "/viewer/camstack";
32458
- /** True when `baseUrl` names the CamStack app link rather than a hub address. */
32459
- function isAppLinkBase(baseUrl) {
32460
- if (baseUrl === null || baseUrl === void 0 || baseUrl === "") return false;
32461
- try {
32462
- return new URL(baseUrl).origin === CAMSTACK_APP_LINK_ORIGIN;
32463
- } catch {
32464
- return false;
32465
- }
32466
- }
32467
- /**
32468
- * The base a viewer link is built on, from the flagged notification address:
32469
- * the effective address is the explicit choice, else AUTO's `resolved`. The
32470
- * one reading of that answer — the NC's tap and the Network page's preview
32471
- * both go through it.
32472
- */
32473
- function viewerLinkBaseOf(endpoint) {
32474
- const flagged = endpoint.baseUrl ?? endpoint.resolved;
32475
- return isAppLinkBase(flagged) ? {
32476
- form: "app",
32477
- hubBaseUrl: endpoint.appLinkHub ?? null
32478
- } : {
32479
- form: "hub",
32480
- hubBaseUrl: flagged
32481
- };
32482
- }
32483
- /**
32484
- * Reduce a base URL to the bare origin the central page accepts as `hub=`,
32485
- * or `null` when it cannot be one: not http(s), carries credentials, or does
32486
- * not parse. A path/query/fragment is dropped — the central page mounts the
32487
- * viewer under the origin itself.
32488
- */
32489
- function toAppLinkHubOrigin(baseUrl) {
32490
- let parsed;
32491
- try {
32492
- parsed = new URL(baseUrl);
32493
- } catch {
32494
- return null;
32495
- }
32496
- if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
32497
- if (parsed.username !== "" || parsed.password !== "") return null;
32498
- if (parsed.hostname === "") return null;
32499
- return parsed.origin;
32500
- }
32501
- /**
32502
- * The hub address an app link carries as `hub=`, chosen from the hub's OWN
32503
- * advertised palette so it always agrees with what the Network page lists:
32504
- * the public tunnel first, then the mesh (Tailscale MagicDNS) — HTTPS only,
32505
- * because the app and the hosted viewer reach it from off the LAN. `null`
32506
- * when the hub advertises neither.
32507
- */
32508
- function pickAppLinkHub(endpoints) {
32509
- const rank = (e) => {
32510
- if (e.kind !== "public" || !e.baseUrl.startsWith("https://")) return null;
32511
- if (e.interfaceKind === "public") return 0;
32512
- if (e.interfaceKind === "vpn") return 1;
32513
- return null;
32514
- };
32515
- return endpoints.flatMap((e) => {
32516
- const r = rank(e);
32517
- const origin = toAppLinkHubOrigin(e.baseUrl);
32518
- return r === null || origin === null ? [] : [{
32519
- r,
32520
- p: e.priority,
32521
- origin
32522
- }];
32523
- }).toSorted((a, b) => a.r - b.r || a.p - b.p)[0]?.origin ?? null;
32524
- }
32525
- /** The viewer route for a destination, or null when it names nothing. */
32526
- function viewerRoute(destination) {
32527
- switch (destination.kind) {
32528
- case "camera": {
32529
- if (!Number.isInteger(destination.deviceId) || destination.deviceId <= 0) return null;
32530
- const at = destination.atMs;
32531
- return {
32532
- path: `/camera/${String(destination.deviceId)}`,
32533
- query: at !== void 0 && Number.isFinite(at) ? [["at", String(Math.trunc(at))]] : []
32534
- };
32535
- }
32536
- case "events": return {
32537
- path: "/events",
32538
- query: []
32539
- };
32540
- case "notifications": return {
32541
- path: "/notifications",
32542
- query: []
32543
- };
32544
- }
32545
- }
32546
- function encodeQuery(query) {
32547
- if (query.length === 0) return "";
32548
- return `?${query.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&")}`;
32549
- }
32550
- /**
32551
- * The link for a viewer destination, or `null` when none can be minted: an
32552
- * unknown destination, or the `hub` form with no reachable base (a dead link
32553
- * is worse than none).
32554
- */
32555
- function buildViewerLink(input) {
32556
- const route = viewerRoute(input.destination);
32557
- if (route === null) return null;
32558
- const raw = input.base.hubBaseUrl;
32559
- const base = raw === null ? "" : raw.replace(/\/+$/, "");
32560
- if (input.base.form === "app") {
32561
- const origin = base === "" ? null : toAppLinkHubOrigin(base);
32562
- const query = origin === null ? route.query : [...route.query, ["hub", origin]];
32563
- return `${CAMSTACK_APP_LINK_ORIGIN}${route.path}${encodeQuery(query)}`;
32564
- }
32565
- if (base === "" || isAppLinkBase(base)) return null;
32566
- return `${base}${HUB_VIEWER_MOUNT}${route.path}${encodeQuery(route.query)}`;
32567
- }
32568
- //#endregion
32569
33016
  //#region src/capabilities/webrtc-session.cap.ts
32570
33017
  /**
32571
33018
  * Optional client-side hints sent at session creation to help the provider
@@ -33073,6 +33520,35 @@ var webrtcSessionCapability = {
33073
33520
  alive: zod.z.boolean()
33074
33521
  })),
33075
33522
  /**
33523
+ * The viewer's downlink probe, for ONE live session (D733).
33524
+ *
33525
+ * The player measures `/adaptive-probe` after the session's first decoded
33526
+ * frame (D635). It used to act on the reading itself, by opening a SECOND
33527
+ * session with `hints.downlinkMbps` — every adaptive live open became two
33528
+ * sessions, two ICE + DTLS handshakes and two server pipelines. It now
33529
+ * hands the measurement to the session it already has; the provider picks
33530
+ * the rung exactly as it would from an offer's hints (same viewport
33531
+ * ceiling) and moves the session there — in place when the negotiated
33532
+ * codec carries the new rendition, by the existing re-offer
33533
+ * (`getSessionState.pendingRenegotiation`) when it cannot.
33534
+ *
33535
+ * A MEASUREMENT, never a tier: the client does not choose. A probe only
33536
+ * raises the rung; lowering it stays the ladder's call on delivery.
33537
+ * `held` answers why nothing moved (`not-adaptive`, `pinned`,
33538
+ * `already-there`, `recorded`, …) — a client that gets ANY answer must not
33539
+ * fall back to a reconnect; only a provider that does not know the method
33540
+ * (an older hub) leaves the client its legacy second session.
33541
+ */
33542
+ applyDownlinkProbe: require_sleep.method(zod.z.object({
33543
+ deviceId: zod.z.number().int().nonnegative(),
33544
+ sessionId: zod.z.string(),
33545
+ downlinkMbps: zod.z.number().nonnegative()
33546
+ }), zod.z.object({
33547
+ outcome: zod.z.enum(["switching", "held"]),
33548
+ tier: require_sleep.CamProfileSchema.nullable(),
33549
+ reason: zod.z.string()
33550
+ }), { kind: "mutation" }),
33551
+ /**
33076
33552
  * The SERVER's half of the stream-debug overlay, for one live session.
33077
33553
  *
33078
33554
  * The client can measure its own transport (`getStats()`); it cannot see
@@ -37494,7 +37970,8 @@ var ConnectionEndpointSchema = zod.z.object({
37494
37970
  "lan-ipv6",
37495
37971
  "public",
37496
37972
  "loopback",
37497
- "app-link"
37973
+ "app-link",
37974
+ "custom"
37498
37975
  ]),
37499
37976
  /**
37500
37977
  * Originating-interface kind — `lan`/`wifi`/`docker`/`vpn`/`other`/
@@ -37596,6 +38073,82 @@ var ViewerEndpointsSchema = zod.z.object({
37596
38073
  /** What the ranking currently resolves to (may be empty if nothing is up). */
37597
38074
  resolved: zod.z.array(zod.z.string()).readonly()
37598
38075
  });
38076
+ /**
38077
+ * DEPRECATED compatibility shape of `getHubAddresses` (D729 amendment, kept
38078
+ * one release for addons built against it): `cluster` is the `agents` role,
38079
+ * `browser` the `signIn` role; `override` is the flagged address.
38080
+ */
38081
+ var HubAddressSnapshotSchema = zod.z.object({
38082
+ override: zod.z.string().nullable(),
38083
+ resolved: zod.z.string().nullable(),
38084
+ source: zod.z.enum([
38085
+ "override",
38086
+ "auto",
38087
+ "none"
38088
+ ])
38089
+ });
38090
+ var HubAddressesSchema = zod.z.object({
38091
+ cluster: HubAddressSnapshotSchema,
38092
+ browser: HubAddressSnapshotSchema
38093
+ });
38094
+ /**
38095
+ * What an address is used for (`hub-address.ts`). `viewer` is many-valued;
38096
+ * `notifications`, `agents` and `signIn` are carried by at most one address.
38097
+ */
38098
+ var HubAddressRoleSchema = zod.z.enum([
38099
+ "viewer",
38100
+ "notifications",
38101
+ "agents",
38102
+ "signIn"
38103
+ ]);
38104
+ var RoleSourceSchema = zod.z.enum([
38105
+ "flagged",
38106
+ "auto",
38107
+ "none"
38108
+ ]);
38109
+ /** One single-valued role: the flagged address, the one in use, and why. */
38110
+ var HubAddressRoleResolutionSchema = zod.z.object({
38111
+ /** The address carrying the flag, or null = automatic. */
38112
+ flagged: zod.z.string().nullable(),
38113
+ /** The address in use, or null when nothing qualifies (never loopback). */
38114
+ resolved: zod.z.string().nullable(),
38115
+ source: RoleSourceSchema
38116
+ });
38117
+ var HubViewerResolutionSchema = zod.z.object({
38118
+ /** The addresses flagged `viewer`, or empty = automatic. */
38119
+ flagged: zod.z.array(zod.z.string()).readonly(),
38120
+ /** What the apps race right now. */
38121
+ resolved: zod.z.array(zod.z.string()).readonly(),
38122
+ source: RoleSourceSchema
38123
+ });
38124
+ /**
38125
+ * One row of the Network page's Addresses table: a detected endpoint, an
38126
+ * operator-entered custom address, or the CamStack app link — flagged the
38127
+ * same way. A URL that is both detected and entered is ONE row.
38128
+ */
38129
+ var HubAddressRowSchema = ConnectionEndpointSchema.extend({
38130
+ /** In the hub's detected palette right now. */
38131
+ detected: zod.z.boolean(),
38132
+ /** Entered by the operator (removable). */
38133
+ custom: zod.z.boolean(),
38134
+ /** The roles explicitly flagged on this address. */
38135
+ flags: zod.z.array(HubAddressRoleSchema).readonly(),
38136
+ /** The roles this address serves right now — flagged or automatic. */
38137
+ inUse: zod.z.array(HubAddressRoleSchema).readonly(),
38138
+ /** The roles this address may be flagged with. */
38139
+ eligible: zod.z.array(HubAddressRoleSchema).readonly()
38140
+ });
38141
+ var HubAddressBookSchema = zod.z.object({
38142
+ rows: zod.z.array(HubAddressRowSchema).readonly(),
38143
+ roles: zod.z.object({
38144
+ viewer: HubViewerResolutionSchema,
38145
+ notifications: HubAddressRoleResolutionSchema,
38146
+ agents: HubAddressRoleResolutionSchema,
38147
+ signIn: HubAddressRoleResolutionSchema
38148
+ }),
38149
+ /** The `hub=` an app link carries (D709), or null. */
38150
+ appLinkHub: zod.z.string().nullable()
38151
+ });
37599
38152
  var AllowedAddressesSchema = zod.z.object({
37600
38153
  /**
37601
38154
  * Allowlist of interface addresses operators have explicitly opted
@@ -37708,26 +38261,78 @@ var localNetworkCapability = {
37708
38261
  */
37709
38262
  getNotificationEndpoint: require_sleep.method(zod.z.void(), NotificationEndpointSchema),
37710
38263
  /**
37711
- * Mark which endpoint notification artifacts use. `baseUrl: null` restores
37712
- * AUTO. The value is stored verbatim (not an index into the candidate
37713
- * list): interfaces come and go, and an index would silently re-point at a
37714
- * different address after a reboot.
38264
+ * @deprecated Use `setAddressRole({ role: 'notifications', … })`. Kept so
38265
+ * an older admin UI keeps working: it writes the same `notifications`
38266
+ * flag, with the same validation. `baseUrl: null` restores automatic; an
38267
+ * address that is not detected becomes a custom address.
37715
38268
  */
37716
38269
  setNotificationEndpoint: require_sleep.method(zod.z.object({ baseUrl: zod.z.string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }),
37717
38270
  /**
37718
- * The endpoints the SDK / viewer races for API access. Empty `baseUrls`
37719
- * means AUTO: every LAN IPv4 address plus the public tunnel, never IPv6,
37720
- * never mesh, never loopback. `resolved` is that set (or the operator's
37721
- * explicit subset) as it stands right now.
38271
+ * The `viewer` role as shipped apps read it: the flagged addresses
38272
+ * (detected or custom), or empty for AUTO — every LAN IPv4 address plus
38273
+ * the public tunnel, never IPv6, mesh, loopback or a custom address.
38274
+ * `resolved` is the set as it stands right now; the custom ones in it are
38275
+ * also in `getConnectionEndpoints`, which the apps filter by it.
37722
38276
  */
37723
38277
  getViewerEndpoints: require_sleep.method(zod.z.void(), ViewerEndpointsSchema),
37724
38278
  /**
37725
- * Replace the viewer race set. Empty `baseUrls` restores AUTO. Stored
37726
- * verbatim (not indices) so a temporarily-down tunnel is not silently
37727
- * dropped from the operator's choice.
38279
+ * @deprecated Use `setAddressRole({ role: 'viewer', … })`. Kept so an
38280
+ * older admin UI keeps working: it replaces the same `viewer` flags.
38281
+ * Empty `baseUrls` restores automatic.
37728
38282
  */
37729
38283
  setViewerEndpoints: require_sleep.method(zod.z.object({ baseUrls: zod.z.array(zod.z.string()).readonly() }), ViewerEndpointsSchema, { kind: "mutation" }),
37730
38284
  /**
38285
+ * The Network page's Addresses table and every role's answer — the ONE
38286
+ * reading of "which address is this hub reached at, for what": detected
38287
+ * endpoints and custom addresses with their role flags, and per role the
38288
+ * flagged address, the one in use and whether it is automatic. Agent
38289
+ * deliveries, sign-in and LAN devices read `roles.agents` / `roles.signIn`
38290
+ * here; nothing ranks the palette itself. `scheme` is the one the caller
38291
+ * shows LAN rows in (as `getConnectionEndpoints`); agents and sign-in are
38292
+ * always resolved over the HTTPS palette.
38293
+ */
38294
+ getAddresses: require_sleep.method(zod.z.object({ scheme: zod.z.enum(["http", "https"]).optional() }).optional(), HubAddressBookSchema),
38295
+ /**
38296
+ * Add a custom address. Validated: an http(s) origin with a host, no
38297
+ * credentials, no path, never loopback — an invalid one is refused with
38298
+ * the reason and nothing is stored. Adding an address that already exists
38299
+ * only updates its label.
38300
+ */
38301
+ addCustomAddress: require_sleep.method(zod.z.object({
38302
+ baseUrl: zod.z.string(),
38303
+ label: zod.z.string().max(80)
38304
+ }), HubAddressBookSchema, {
38305
+ kind: "mutation",
38306
+ auth: "admin"
38307
+ }),
38308
+ /** Remove a custom address. Its role flags go with it (those roles turn automatic). */
38309
+ removeCustomAddress: require_sleep.method(zod.z.object({ baseUrl: zod.z.string() }), HubAddressBookSchema, {
38310
+ kind: "mutation",
38311
+ auth: "admin"
38312
+ }),
38313
+ /**
38314
+ * Flag (`enabled: true`) or unflag an address for a role. For a
38315
+ * single-valued role, flagging moves the flag off every other address.
38316
+ * `baseUrl: null` returns the role to automatic. The address must be a row
38317
+ * of the table (detected, custom, or the app link for notifications);
38318
+ * `signIn` on a plain-http address is refused unless `acceptInsecureHttp`.
38319
+ */
38320
+ setAddressRole: require_sleep.method(zod.z.object({
38321
+ role: HubAddressRoleSchema,
38322
+ baseUrl: zod.z.string().nullable(),
38323
+ enabled: zod.z.boolean(),
38324
+ acceptInsecureHttp: zod.z.boolean().optional()
38325
+ }), HubAddressBookSchema, {
38326
+ kind: "mutation",
38327
+ auth: "admin"
38328
+ }),
38329
+ /**
38330
+ * @deprecated Read `getAddresses().roles.agents` / `.signIn`. Kept one
38331
+ * release so an addon built against the D729 amendment keeps resolving:
38332
+ * `cluster` = the `agents` role, `browser` = the `signIn` role.
38333
+ */
38334
+ getHubAddresses: require_sleep.method(zod.z.void(), HubAddressesSchema),
38335
+ /**
37731
38336
  * Read the operator's allowlist. Empty = "auto" (no filter). Used
37732
38337
  * by the admin UI's address selector to seed its checkbox state.
37733
38338
  */
@@ -47869,7 +48474,9 @@ var FRIGATE_PLUS_TO_MACRO = {
47869
48474
  * - `attribute`: Frigate+ emits these INSIDE a parent box (a face inside a
47870
48475
  * person, a plate or a carrier logo on a vehicle). Mapping one to its
47871
48476
  * parent's macro would spawn a second, overlapping track for the same
47872
- * subject; CamStack derives faces and plates with its own detail steps.
48477
+ * subject. `face` and `license_plate` are instead declared as CROP supplies
48478
+ * (D730): the box becomes a detail child of the person / vehicle containing
48479
+ * it, when the face / plate step uses the root model.
47873
48480
  * - `no-macro`: an object with no detection macro to live under.
47874
48481
  */
47875
48482
  var FRIGATE_PLUS_UNMAPPED_LABELS = {
@@ -47915,17 +48522,18 @@ function buildFrigateClassMap(labels) {
47915
48522
  };
47916
48523
  }
47917
48524
  /**
47918
- * Everything a Frigate / Frigate+ entry derives from its labelmap: the classMap
47919
- * and the steps it fills (D715). Every Frigate builder spreads this, so the two
47920
- * cannot be derived from different label lists. A COCO labelmap gets its map
47921
- * and no fill.
48525
+ * Everything a Frigate / Frigate+ entry derives from ITS OWN labelmap: the
48526
+ * classMap and the steps it can supply (D730) — label and crop kinds alike.
48527
+ * Every Frigate builder spreads this at import, so the two cannot be derived
48528
+ * from different label lists, and no family is assumed to carry a label it
48529
+ * does not list.
47922
48530
  */
47923
48531
  function frigateLabelFields(labels) {
47924
48532
  const classMap = buildFrigateClassMap(labels);
47925
- const fills = deriveClassifierFills(labels, classMap);
47926
- return fills.length > 0 ? {
48533
+ const supplies = deriveRootSupplies(labels, classMap);
48534
+ return supplies.length > 0 ? {
47927
48535
  classMap,
47928
- fillsClassifierEffects: fills
48536
+ suppliesSteps: supplies
47929
48537
  } : { classMap };
47930
48538
  }
47931
48539
  //#endregion
@@ -53367,12 +53975,24 @@ var METHOD_ACCESS_MAP = Object.freeze({
53367
53975
  addonId: null,
53368
53976
  access: "view"
53369
53977
  },
53978
+ "localNetwork.addCustomAddress": {
53979
+ capName: "local-network",
53980
+ capScope: "system",
53981
+ addonId: null,
53982
+ access: "create"
53983
+ },
53370
53984
  "localNetwork.downloadCa": {
53371
53985
  capName: "local-network",
53372
53986
  capScope: "system",
53373
53987
  addonId: null,
53374
53988
  access: "view"
53375
53989
  },
53990
+ "localNetwork.getAddresses": {
53991
+ capName: "local-network",
53992
+ capScope: "system",
53993
+ addonId: null,
53994
+ access: "view"
53995
+ },
53376
53996
  "localNetwork.getAllowedAddresses": {
53377
53997
  capName: "local-network",
53378
53998
  capScope: "system",
@@ -53385,6 +54005,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
53385
54005
  addonId: null,
53386
54006
  access: "view"
53387
54007
  },
54008
+ "localNetwork.getHubAddresses": {
54009
+ capName: "local-network",
54010
+ capScope: "system",
54011
+ addonId: null,
54012
+ access: "view"
54013
+ },
53388
54014
  "localNetwork.getNotificationEndpoint": {
53389
54015
  capName: "local-network",
53390
54016
  capScope: "system",
@@ -53421,6 +54047,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
53421
54047
  addonId: null,
53422
54048
  access: "create"
53423
54049
  },
54050
+ "localNetwork.removeCustomAddress": {
54051
+ capName: "local-network",
54052
+ capScope: "system",
54053
+ addonId: null,
54054
+ access: "delete"
54055
+ },
53424
54056
  "localNetwork.resetAllowlistToBestMatch": {
53425
54057
  capName: "local-network",
53426
54058
  capScope: "system",
@@ -53433,6 +54065,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
53433
54065
  addonId: null,
53434
54066
  access: "create"
53435
54067
  },
54068
+ "localNetwork.setAddressRole": {
54069
+ capName: "local-network",
54070
+ capScope: "system",
54071
+ addonId: null,
54072
+ access: "create"
54073
+ },
53436
54074
  "localNetwork.setAllowedAddresses": {
53437
54075
  capName: "local-network",
53438
54076
  capScope: "system",
@@ -57687,6 +58325,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
57687
58325
  addonId: null,
57688
58326
  access: "create"
57689
58327
  },
58328
+ "webrtcSession.applyDownlinkProbe": {
58329
+ capName: "webrtc-session",
58330
+ capScope: "device",
58331
+ addonId: null,
58332
+ access: "create"
58333
+ },
57690
58334
  "webrtcSession.closeSession": {
57691
58335
  capName: "webrtc-session",
57692
58336
  capScope: "device",
@@ -64961,6 +65605,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
64961
65605
  form: "single",
64962
65606
  optional: false
64963
65607
  }],
65608
+ "webrtcSession.applyDownlinkProbe": [{
65609
+ name: "deviceId",
65610
+ form: "single",
65611
+ optional: false
65612
+ }],
64964
65613
  "webrtcSession.closeSession": [{
64965
65614
  name: "deviceId",
64966
65615
  form: "single",
@@ -66001,6 +66650,11 @@ function createSystemProxy(api) {
66001
66650
  setNotificationEndpoint: (input) => dispatch("localNetwork", "setNotificationEndpoint", "mutation", input),
66002
66651
  getViewerEndpoints: (input) => dispatch("localNetwork", "getViewerEndpoints", "query", input),
66003
66652
  setViewerEndpoints: (input) => dispatch("localNetwork", "setViewerEndpoints", "mutation", input),
66653
+ getAddresses: (input) => dispatch("localNetwork", "getAddresses", "query", input),
66654
+ addCustomAddress: (input) => dispatch("localNetwork", "addCustomAddress", "mutation", input),
66655
+ removeCustomAddress: (input) => dispatch("localNetwork", "removeCustomAddress", "mutation", input),
66656
+ setAddressRole: (input) => dispatch("localNetwork", "setAddressRole", "mutation", input),
66657
+ getHubAddresses: (input) => dispatch("localNetwork", "getHubAddresses", "query", input),
66004
66658
  getAllowedAddresses: (input) => dispatch("localNetwork", "getAllowedAddresses", "query", input),
66005
66659
  setAllowedAddresses: (input) => dispatch("localNetwork", "setAllowedAddresses", "mutation", input),
66006
66660
  resetAllowlistToBestMatch: (input) => dispatch("localNetwork", "resetAllowlistToBestMatch", "mutation", input),
@@ -68962,7 +69616,7 @@ var STATE_SUMMARY_VARS = [
68962
69616
  {
68963
69617
  name: "summarySection",
68964
69618
  label: "Summary: this section",
68965
- description: "The section a per-section summary row is about (D698); on a summary sent as one message, every section it reports, comma-separated.",
69619
+ description: "The section a per-section summary row is about; on a summary sent as one message, every section it reports, comma-separated.",
68966
69620
  example: "Batterie scariche",
68967
69621
  group: "system",
68968
69622
  families: ["rule"],
@@ -71044,6 +71698,33 @@ function isRemovedModelId(stepId, modelId) {
71044
71698
  return REMOVED_MODEL_IDS[stepId]?.includes(modelId) ?? false;
71045
71699
  }
71046
71700
  //#endregion
71701
+ //#region src/pipeline/root-face-crop.ts
71702
+ /** Side of the square, as a multiple of the face box's longer side. */
71703
+ var ROOT_FACE_REFINE_SCALE = 1.6;
71704
+ /**
71705
+ * The input the face detector sees the square at, in pixels. A face that
71706
+ * fills ~60% of a 160 px input is in the detector's measured sweet spot; the
71707
+ * same square fed at 640 puts a ~400 px face in front of a detector whose
71708
+ * largest anchors are far smaller.
71709
+ */
71710
+ var ROOT_FACE_REFINE_INPUT_PX = 160;
71711
+ /**
71712
+ * The square (frame-space pixels) a root face box is refined in: centred on
71713
+ * the box, side `ROOT_FACE_REFINE_SCALE ×` its longer side, slid inside the
71714
+ * frame, and shrunk only when the frame's shorter side is smaller. Pure.
71715
+ */
71716
+ function deriveRootFaceRefineRect(face, frameWidth, frameHeight) {
71717
+ const side = Math.min(Math.max(face.w, face.h) * ROOT_FACE_REFINE_SCALE, Math.min(frameWidth, frameHeight));
71718
+ const cx = face.x + face.w / 2;
71719
+ const cy = face.y + face.h / 2;
71720
+ return {
71721
+ x: Math.min(Math.max(0, cx - side / 2), frameWidth - side),
71722
+ y: Math.min(Math.max(0, cy - side / 2), frameHeight - side),
71723
+ w: side,
71724
+ h: side
71725
+ };
71726
+ }
71727
+ //#endregion
71047
71728
  //#region src/pipeline/detail-crop.ts
71048
71729
  /**
71049
71730
  * THE detail-crop convention — the single derivation of the rectangle a
@@ -73160,6 +73841,7 @@ exports.AUDIO_NOT_CLASSIFIED_REASONS = AUDIO_NOT_CLASSIFIED_REASONS;
73160
73841
  exports.AUDIO_PRESETS = require_composition.AUDIO_PRESETS;
73161
73842
  exports.AUDIO_SCALE_EPOCH_ISO = AUDIO_SCALE_EPOCH_ISO;
73162
73843
  exports.AUDIO_SCALE_EPOCH_MS = AUDIO_SCALE_EPOCH_MS;
73844
+ exports.AUTOMATIC_HUB_ADDRESS_FLAGS = AUTOMATIC_HUB_ADDRESS_FLAGS;
73163
73845
  exports.AccessoriesStatusSchema = AccessoriesStatusSchema;
73164
73846
  exports.AccessoryKind = AccessoryKind;
73165
73847
  exports.AddBrokerInputSchema = AddBrokerInputSchema;
@@ -73323,6 +74005,8 @@ exports.CONNECTION_TEST_TIMEOUT_MS = CONNECTION_TEST_TIMEOUT_MS;
73323
74005
  exports.CONTAINER_CHILD_PRIORITY = CONTAINER_CHILD_PRIORITY;
73324
74006
  exports.CORE_BLOCKS_ADDON_ID = CORE_BLOCKS_ADDON_ID;
73325
74007
  exports.CORE_BLOCK_ADDON_PREFIX = CORE_BLOCK_ADDON_PREFIX;
74008
+ exports.CROP_SUPPLY_SOURCES = CROP_SUPPLY_SOURCES;
74009
+ exports.CROP_SUPPLY_STEP_IDS = CROP_SUPPLY_STEP_IDS;
73326
74010
  exports.CUSTOMIZATION_BLOCK_NAME_PREFIX = require_composition.CUSTOMIZATION_BLOCK_NAME_PREFIX;
73327
74011
  exports.CalendarColorSchema = CalendarColorSchema;
73328
74012
  exports.CalendarEntrySchema = CalendarEntrySchema;
@@ -73515,7 +74199,6 @@ exports.DEFAULT_EVENTS_BAND_BUFFER_SEC = DEFAULT_EVENTS_BAND_BUFFER_SEC;
73515
74199
  exports.DEFAULT_EVENT_COLOR = DEFAULT_EVENT_COLOR;
73516
74200
  exports.DEFAULT_FEATURES = DEFAULT_FEATURES;
73517
74201
  exports.DEFAULT_FIRST_SIGHTING_FRESHNESS_MS = require_composition.DEFAULT_FIRST_SIGHTING_FRESHNESS_MS;
73518
- exports.DEFAULT_LABEL_LINK_POLICY = DEFAULT_LABEL_LINK_POLICY;
73519
74202
  exports.DEFAULT_MIN_LANDMARK_FACE_SIZE_PX = DEFAULT_MIN_LANDMARK_FACE_SIZE_PX;
73520
74203
  exports.DEFAULT_NATIVE_LEASE_SETTINGS = DEFAULT_NATIVE_LEASE_SETTINGS;
73521
74204
  exports.DEFAULT_OVERLAY_LAYERS = DEFAULT_OVERLAY_LAYERS;
@@ -73692,9 +74375,6 @@ exports.ExtensionSourceSchema = ExtensionSourceSchema;
73692
74375
  exports.FEEDER_EXAMPLE_ADDON_ID = FEEDER_EXAMPLE_ADDON_ID;
73693
74376
  exports.FEEDER_EXAMPLE_HA_DEVICE_ID = FEEDER_EXAMPLE_HA_DEVICE_ID;
73694
74377
  exports.FFMPEG_PATH_ENV = require_composition.FFMPEG_PATH_ENV;
73695
- exports.FILLABLE_STEP_IDS = FILLABLE_STEP_IDS;
73696
- exports.FILLABLE_STEP_MACRO = FILLABLE_STEP_MACRO;
73697
- exports.FILLABLE_STEP_VOCABULARIES = FILLABLE_STEP_VOCABULARIES;
73698
74378
  exports.FIRST_LEVEL_MACRO_CLASSES = FIRST_LEVEL_MACRO_CLASSES;
73699
74379
  exports.FRIGATE_PLUS_TO_MACRO = FRIGATE_PLUS_TO_MACRO;
73700
74380
  exports.FRIGATE_PLUS_UNMAPPED_LABELS = FRIGATE_PLUS_UNMAPPED_LABELS;
@@ -73708,7 +74388,6 @@ exports.FeatureManifestSchema = FeatureManifestSchema;
73708
74388
  exports.FeatureProbeStatusSchema = FeatureProbeStatusSchema;
73709
74389
  exports.FieldClaimModeSchema = FieldClaimModeSchema;
73710
74390
  exports.FieldClaimSchema = FieldClaimSchema;
73711
- exports.FillsClassifierEffectSchema = FillsClassifierEffectSchema;
73712
74391
  exports.FloodStatusSchema = FloodStatusSchema;
73713
74392
  exports.Fmp4BoxSplitter = require_composition.Fmp4BoxSplitter;
73714
74393
  exports.FrameHandleFormatSchema = require_sleep.FrameHandleFormatSchema;
@@ -73730,12 +74409,14 @@ exports.HAP_KEYFRAME_INTERVAL_SEC = HAP_KEYFRAME_INTERVAL_SEC;
73730
74409
  exports.HF_BASE_URL = HF_BASE_URL;
73731
74410
  exports.HF_REPO = HF_REPO;
73732
74411
  exports.HOURS_PER_DAY = HOURS_PER_DAY;
74412
+ exports.HUB_ADDRESS_ROLES = HUB_ADDRESS_ROLES;
73733
74413
  exports.HUB_VIEWER_MOUNT = HUB_VIEWER_MOUNT;
73734
74414
  exports.HWACCEL_OPTIONS = HWACCEL_OPTIONS;
73735
74415
  exports.HealthStatusSchema = HealthStatusSchema;
73736
74416
  exports.HfModelResolutionSchema = HfModelResolutionSchema;
73737
74417
  exports.HistoryPointSchema = HistoryPointSchema;
73738
74418
  exports.HistoryResolutionEnum = HistoryResolutionEnum;
74419
+ exports.HubAddressRoleSchema = HubAddressRoleSchema;
73739
74420
  exports.HumidifierStatusSchema = HumidifierStatusSchema;
73740
74421
  exports.HumiditySensorStatusSchema = HumiditySensorStatusSchema;
73741
74422
  exports.HvacModeSchema = HvacModeSchema;
@@ -73757,8 +74438,9 @@ exports.IntercomAbilitySchema = IntercomAbilitySchema;
73757
74438
  exports.IntercomStatusSchema = IntercomStatusSchema;
73758
74439
  exports.KNOWN_CAP_NAMES = KNOWN_CAP_NAMES;
73759
74440
  exports.KeyEventSchema = KeyEventSchema;
73760
- exports.LABEL_LINK_POLICIES = LABEL_LINK_POLICIES;
73761
- exports.LABEL_LINK_SETTING_KEY = LABEL_LINK_SETTING_KEY;
74441
+ exports.LABEL_SUPPLY_MACRO = LABEL_SUPPLY_MACRO;
74442
+ exports.LABEL_SUPPLY_STEP_IDS = LABEL_SUPPLY_STEP_IDS;
74443
+ exports.LABEL_SUPPLY_VOCABULARIES = LABEL_SUPPLY_VOCABULARIES;
73762
74444
  exports.LEGACY_READ_ONLY_CONFIG_KEY = LEGACY_READ_ONLY_CONFIG_KEY;
73763
74445
  exports.LINKED_DEVICES_SLOT = LINKED_DEVICES_SLOT;
73764
74446
  exports.LINKED_LABEL_STEP_PREFIX = LINKED_LABEL_STEP_PREFIX;
@@ -74282,6 +74964,10 @@ exports.ROOM_EXAMPLE_MOTION_SOURCES = ROOM_EXAMPLE_MOTION_SOURCES;
74282
74964
  exports.ROOM_EXAMPLE_TEMPERATURE_SOURCES = ROOM_EXAMPLE_TEMPERATURE_SOURCES;
74283
74965
  exports.ROOM_OCCUPANCY_WINDOW_MS = ROOM_OCCUPANCY_WINDOW_MS;
74284
74966
  exports.ROOT_BUCKET_KEY = ROOT_BUCKET_KEY;
74967
+ exports.ROOT_CHILD_CONTAINMENT_MIN = ROOT_CHILD_CONTAINMENT_MIN;
74968
+ exports.ROOT_FACE_REFINE_INPUT_PX = ROOT_FACE_REFINE_INPUT_PX;
74969
+ exports.ROOT_FACE_REFINE_SCALE = ROOT_FACE_REFINE_SCALE;
74970
+ exports.ROOT_SUPPLIABLE_STEP_IDS = ROOT_SUPPLIABLE_STEP_IDS;
74285
74971
  exports.RUNTIME_DEFAULTS = RUNTIME_DEFAULTS;
74286
74972
  exports.RUNTIME_STATE_POLICY = RUNTIME_STATE_POLICY;
74287
74973
  exports.RUNTIME_STATE_REFRESH_MISS_COOLDOWN_MS = RUNTIME_STATE_REFRESH_MISS_COOLDOWN_MS;
@@ -74366,6 +75052,7 @@ exports.RetrainStatusSchema = RetrainStatusSchema;
74366
75052
  exports.RetrainTrackSchema = RetrainTrackSchema;
74367
75053
  exports.RetrainTransitionResultSchema = RetrainTransitionResultSchema;
74368
75054
  exports.RingBuffer = RingBuffer;
75055
+ exports.RootStepSupplySchema = RootStepSupplySchema;
74369
75056
  exports.RtpSourceSchema = RtpSourceSchema;
74370
75057
  exports.RtspRestreamEntrySchema = RtspRestreamEntrySchema;
74371
75058
  exports.RunnerCameraConfigSchema = RunnerCameraConfigSchema;
@@ -74389,6 +75076,8 @@ exports.SCOPE_PRESETS = SCOPE_PRESETS;
74389
75076
  exports.SENSOR_FEATURES = SENSOR_FEATURES;
74390
75077
  exports.SENSOR_MAP = SENSOR_MAP;
74391
75078
  exports.SHARE_VIEW_KINDS = require_sleep.SHARE_VIEW_KINDS;
75079
+ exports.SIGN_IN_HTTP_REASON = SIGN_IN_HTTP_REASON;
75080
+ exports.SINGLE_HUB_ADDRESS_ROLES = SINGLE_HUB_ADDRESS_ROLES;
74392
75081
  exports.SOURCE_CAPS = require_composition.SOURCE_CAPS;
74393
75082
  exports.SOURCE_CAP_ACTIVE_FIELD = require_composition.SOURCE_CAP_ACTIVE_FIELD;
74394
75083
  exports.SOURCE_CAP_CHANGED_AT_FIELD = require_composition.SOURCE_CAP_CHANGED_AT_FIELD;
@@ -74517,6 +75206,7 @@ exports.SubscribeAudioChunksInputSchema = require_sleep.SubscribeAudioChunksInpu
74517
75206
  exports.SubscribeAudioChunksResultSchema = require_sleep.SubscribeAudioChunksResultSchema;
74518
75207
  exports.SubscribeFramesInputSchema = require_sleep.SubscribeFramesInputSchema;
74519
75208
  exports.SubscribeFramesResultSchema = require_sleep.SubscribeFramesResultSchema;
75209
+ exports.SuppliedRootOutputSchema = SuppliedRootOutputSchema;
74520
75210
  exports.SwitchStatusSchema = SwitchStatusSchema;
74521
75211
  exports.SystemMetricsSchema = SystemMetricsSchema;
74522
75212
  exports.SystemMirror = SystemMirror;
@@ -74574,6 +75264,8 @@ exports.TransportPlaneSchema = TransportPlaneSchema;
74574
75264
  exports.TurnServerSchema = TurnServerSchema;
74575
75265
  exports.UNATTRIBUTED_BUCKET_KEY = UNATTRIBUTED_BUCKET_KEY;
74576
75266
  exports.UNIT_TABLE = UNIT_TABLE;
75267
+ exports.USE_ROOT_MODEL_DEFAULTS = USE_ROOT_MODEL_DEFAULTS;
75268
+ exports.USE_ROOT_MODEL_SETTING_KEY = USE_ROOT_MODEL_SETTING_KEY;
74577
75269
  exports.UnifiedBrokerInfoSchema = BrokerInfoSchema;
74578
75270
  exports.UnitConversionError = UnitConversionError;
74579
75271
  exports.UnstampedEventMediaCountSchema = UnstampedEventMediaCountSchema;
@@ -74668,6 +75360,7 @@ exports.alarmPatchLanded = alarmPatchLanded;
74668
75360
  exports.alarmProfileById = alarmProfileById;
74669
75361
  exports.alertsCapability = alertsCapability;
74670
75362
  exports.allDoActions = allDoActions;
75363
+ exports.allowedSuppliedEffect = allowedSuppliedEffect;
74671
75364
  exports.ambientLightSensorCapability = ambientLightSensorCapability;
74672
75365
  exports.applyBlockingEdit = applyBlockingEdit;
74673
75366
  exports.applyModeDelaysEdits = applyModeDelaysEdits;
@@ -74732,6 +75425,7 @@ exports.canConvertUnit = canConvertUnit;
74732
75425
  exports.canonicalEgressPlan = canonicalEgressPlan;
74733
75426
  exports.carbonMonoxideCapability = carbonMonoxideCapability;
74734
75427
  exports.cellsToRects = cellsToRects;
75428
+ exports.checkRoleAddress = checkRoleAddress;
74735
75429
  exports.classifyBearerPrincipal = classifyBearerPrincipal;
74736
75430
  exports.classifyStream = classifyStream;
74737
75431
  exports.classifyStreams = classifyStreams;
@@ -74774,6 +75468,7 @@ exports.connectivityCapability = connectivityCapability;
74774
75468
  exports.consumableLowPercentOf = consumableLowPercentOf;
74775
75469
  exports.consumablesCapability = consumablesCapability;
74776
75470
  exports.contactCapability = contactCapability;
75471
+ exports.containingParentIndex = containingParentIndex;
74777
75472
  exports.controlCapability = controlCapability;
74778
75473
  exports.convertPresetFeature = convertPresetFeature;
74779
75474
  exports.convertUnit = convertUnit;
@@ -74817,15 +75512,18 @@ exports.defineExtensionSlotProvider = defineExtensionSlotProvider;
74817
75512
  exports.deleteAlarmProfile = deleteAlarmProfile;
74818
75513
  exports.deriveBatteryPresence = deriveBatteryPresence;
74819
75514
  exports.deriveCameraSwitches = deriveCameraSwitches;
74820
- exports.deriveClassifierFills = deriveClassifierFills;
74821
75515
  exports.deriveDetailCropRect = deriveDetailCropRect;
74822
75516
  exports.derivePostTriggeredAction = derivePostTriggeredAction;
74823
75517
  exports.deriveRecordingMode = deriveRecordingMode;
75518
+ exports.deriveRootFaceRefineRect = deriveRootFaceRefineRect;
75519
+ exports.deriveRootSupplies = deriveRootSupplies;
74824
75520
  exports.describeComposableCaps = describeComposableCaps;
74825
75521
  exports.describeCustomActions = require_sleep.describeCustomActions;
74826
75522
  exports.describeModelVariant = describeModelVariant;
74827
75523
  exports.describeOnboardClamp = describeOnboardClamp;
74828
75524
  exports.describeOnboardRefusal = describeOnboardRefusal;
75525
+ exports.describeRootSupplies = describeRootSupplies;
75526
+ exports.describeRootSupply = describeRootSupply;
74829
75527
  exports.describeStateField = describeStateField;
74830
75528
  exports.describeSystemEventDeliveryProblem = describeSystemEventDeliveryProblem;
74831
75529
  exports.describeSystemEventFamilyProblem = describeSystemEventFamilyProblem;
@@ -74909,7 +75607,6 @@ exports.feederExampleSources = feederExampleSources;
74909
75607
  exports.ffmpegRate = ffmpegRate;
74910
75608
  exports.fieldReads = require_composition.fieldReads;
74911
75609
  exports.filesystemBrowseCapability = filesystemBrowseCapability;
74912
- exports.fillFor = fillFor;
74913
75610
  exports.findCandidateCycles = findCandidateCycles;
74914
75611
  exports.findCompositionCycles = findCompositionCycles;
74915
75612
  exports.findTimezone = findTimezone;
@@ -74934,6 +75631,7 @@ exports.gifFromMp4Args = gifFromMp4Args;
74934
75631
  exports.hasMotionTrigger = hasMotionTrigger;
74935
75632
  exports.hfModelUrl = hfModelUrl;
74936
75633
  exports.htmlToText = htmlToText;
75634
+ exports.hubAddressHostname = hubAddressHostname;
74937
75635
  exports.hubMotionNativeProvider = hubMotionNativeProvider;
74938
75636
  exports.humidifierCapability = humidifierCapability;
74939
75637
  exports.humiditySensorCapability = humiditySensorCapability;
@@ -74960,9 +75658,9 @@ exports.isAudioRule = isAudioRule;
74960
75658
  exports.isBaseConditionKey = isBaseConditionKey;
74961
75659
  exports.isBatteryPresenceFault = isBatteryPresenceFault;
74962
75660
  exports.isClusterScopedStep = isClusterScopedStep;
74963
- exports.isCocoLabelmap = isCocoLabelmap;
74964
75661
  exports.isCollectionArrayMethod = isCollectionArrayMethod;
74965
75662
  exports.isDefaultAlarmProfileId = isDefaultAlarmProfileId;
75663
+ exports.isDefaultViewerEndpoint = isDefaultViewerEndpoint;
74966
75664
  exports.isDeployableToAgent = isDeployableToAgent;
74967
75665
  exports.isDetectionMacroClass = isDetectionMacroClass;
74968
75666
  exports.isDeviceConfigCap = require_sleep.isDeviceConfigCap;
@@ -74974,11 +75672,11 @@ exports.isEmbeddingModelFailed = isEmbeddingModelFailed;
74974
75672
  exports.isEvent = require_sleep.isEvent;
74975
75673
  exports.isEventKindLabelKey = isEventKindLabelKey;
74976
75674
  exports.isEventOwnerType = isEventOwnerType;
74977
- exports.isFillableStepId = isFillableStepId;
74978
75675
  exports.isFirstLevelMacroClass = isFirstLevelMacroClass;
74979
75676
  exports.isHeldBackTemplateVar = isHeldBackTemplateVar;
74980
75677
  exports.isIsolatedBuiltin = isIsolatedBuiltin;
74981
75678
  exports.isKnownTemplateVar = isKnownTemplateVar;
75679
+ exports.isLabelSupplyStepId = isLabelSupplyStepId;
74982
75680
  exports.isLocationEnabled = isLocationEnabled;
74983
75681
  exports.isMediaOwnerType = isMediaOwnerType;
74984
75682
  exports.isNode = isNode;
@@ -74988,9 +75686,11 @@ exports.isOwnerOnlyEventCategory = require_event_category.isOwnerOnlyEventCatego
74988
75686
  exports.isRemovedModelId = isRemovedModelId;
74989
75687
  exports.isRestoredCap = isRestoredCap;
74990
75688
  exports.isRetiredClearKey = isRetiredClearKey;
75689
+ exports.isRootSuppliableStepId = isRootSuppliableStepId;
74991
75690
  exports.isSameAddonId = isSameAddonId;
74992
75691
  exports.isScheduleActive = isScheduleActive;
74993
75692
  exports.isSecretConfigField = isSecretConfigField;
75693
+ exports.isSingleHubAddressRole = isSingleHubAddressRole;
74994
75694
  exports.isSoftwareDecode = require_composition.isSoftwareDecode;
74995
75695
  exports.isSourceCap = require_composition.isSourceCap;
74996
75696
  exports.isStateLabelKey = isStateLabelKey;
@@ -75143,11 +75843,11 @@ exports.readDetailCropConvention = readDetailCropConvention;
75143
75843
  exports.readDeviceStateFrom = readDeviceStateFrom;
75144
75844
  exports.readHubMotionDetected = readHubMotionDetected;
75145
75845
  exports.readHubMotionStatus = readHubMotionStatus;
75146
- exports.readLabelLinkPolicy = readLabelLinkPolicy;
75147
75846
  exports.readMotionTriggeredBy = readMotionTriggeredBy;
75148
75847
  exports.readNativeLeaseOverride = readNativeLeaseOverride;
75149
75848
  exports.readNodePin = require_sleep.readNodePin;
75150
75849
  exports.readTimelapseGeneratedAt = readTimelapseGeneratedAt;
75850
+ exports.readUseRootModel = readUseRootModel;
75151
75851
  exports.readinessKey = require_sleep.readinessKey;
75152
75852
  exports.rebootCapability = rebootCapability;
75153
75853
  exports.reconcileBlockDevices = reconcileBlockDevices;
@@ -75167,6 +75867,7 @@ exports.resetPoolBaseline = resetPoolBaseline;
75167
75867
  exports.resolveAddonExecution = resolveAddonExecution;
75168
75868
  exports.resolveAddonGroup = resolveAddonGroup;
75169
75869
  exports.resolveAddonPlacement = resolveAddonPlacement;
75870
+ exports.resolveAgentsAddress = resolveAgentsAddress;
75170
75871
  exports.resolveBucketMs = resolveBucketMs;
75171
75872
  exports.resolveCapMount = require_sleep.resolveCapMount;
75172
75873
  exports.resolveClipSearchMinScore = resolveClipSearchMinScore;
@@ -75180,22 +75881,28 @@ exports.resolveDeviceProfile = resolveDeviceProfile;
75180
75881
  exports.resolveEgressDecodeHwAccel = resolveEgressDecodeHwAccel;
75181
75882
  exports.resolveExportProfiles = resolveExportProfiles;
75182
75883
  exports.resolveFormat = resolveFormat;
75884
+ exports.resolveHubAddressRoles = resolveHubAddressRoles;
75183
75885
  exports.resolveHydratedFieldValue = require_sleep.resolveHydratedFieldValue;
75184
- exports.resolveLabelLink = resolveLabelLink;
75185
75886
  exports.resolveLocationIcon = resolveLocationIcon;
75186
75887
  exports.resolveLocationMode = resolveLocationMode;
75187
75888
  exports.resolveMethodAuth = resolveMethodAuth;
75188
75889
  exports.resolveModeDelays = resolveModeDelays;
75189
75890
  exports.resolveModeNotifyPolicy = resolveModeNotifyPolicy;
75190
75891
  exports.resolveMutate = resolveMutate;
75892
+ exports.resolveNotificationsAddress = resolveNotificationsAddress;
75191
75893
  exports.resolvePoolMemoryPolicy = resolvePoolMemoryPolicy;
75192
75894
  exports.resolveRecordingProfiles = resolveRecordingProfiles;
75895
+ exports.resolveRootSupply = resolveRootSupply;
75193
75896
  exports.resolveRunnerId = resolveRunnerId;
75897
+ exports.resolveSignInAddress = resolveSignInAddress;
75194
75898
  exports.resolveStreamMaps = require_composition.resolveStreamMaps;
75195
75899
  exports.resolveVariantModelId = resolveVariantModelId;
75196
75900
  exports.resolveViewableDeviceIds = resolveViewableDeviceIds;
75901
+ exports.resolveViewerAddresses = resolveViewerAddresses;
75197
75902
  exports.roleSpec = roleSpec;
75198
75903
  exports.roomCompositionExample = roomCompositionExample;
75904
+ exports.rootSuppliesOf = rootSuppliesOf;
75905
+ exports.rootSupplyServes = rootSupplyServes;
75199
75906
  exports.ruleAutoTriggersAlarm = ruleAutoTriggersAlarm;
75200
75907
  exports.ruleBelongsToAlarm = ruleBelongsToAlarm;
75201
75908
  exports.ruleDeviceCompatCapNames = ruleDeviceCompatCapNames;
@@ -75225,6 +75932,7 @@ exports.scriptRunnerCapability = scriptRunnerCapability;
75225
75932
  exports.searchDisplayIcons = searchDisplayIcons;
75226
75933
  exports.secureActuationFor = secureActuationFor;
75227
75934
  exports.selectAssignedProfileSlots = require_sleep.selectAssignedProfileSlots;
75935
+ exports.selectViewerEndpoints = selectViewerEndpoints;
75228
75936
  exports.sendTimesForEditor = sendTimesForEditor;
75229
75937
  exports.sendTimesOfSchedule = sendTimesOfSchedule;
75230
75938
  exports.sensorRaisedOf = sensorRaisedOf;
@@ -75265,6 +75973,7 @@ exports.strippedOfLegacyReadOnly = strippedOfLegacyReadOnly;
75265
75973
  exports.subKindsOf = subKindsOf;
75266
75974
  exports.summarisePrivacyAudio = summarisePrivacyAudio;
75267
75975
  exports.summarizeEffectiveScope = summarizeEffectiveScope;
75976
+ exports.supplyFor = supplyFor;
75268
75977
  exports.supportedRuntimes = supportedRuntimes;
75269
75978
  exports.switchCapability = switchCapability;
75270
75979
  exports.switchedOffIds = switchedOffIds;
@@ -75315,6 +76024,7 @@ exports.userManagementCapability = userManagementCapability;
75315
76024
  exports.userPasskeysCapability = userPasskeysCapability;
75316
76025
  exports.vacuumControlCapability = vacuumControlCapability;
75317
76026
  exports.validateComposition = validateComposition;
76027
+ exports.validateCustomAddress = validateCustomAddress;
75318
76028
  exports.validateExpressionSource = require_composition.validateExpressionSource;
75319
76029
  exports.validateRecipeBounds = validateRecipeBounds;
75320
76030
  exports.valveCapability = valveCapability;