@camstack/addon-admin-ui 1.2.515 → 1.2.517

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 (140) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/assets/{AddonAssetIcon-CBiyVWSK.js → AddonAssetIcon-DaAStKC8.js} +1 -1
  3. package/dist/assets/Addons-Do9U7LcM.js +5 -0
  4. package/dist/assets/{AdminPage-B_wUVHLs.js → AdminPage-CgFhd3My.js} +1 -1
  5. package/dist/assets/{AdminSectionedPage-bjMTXExV.js → AdminSectionedPage-C9702pKD.js} +1 -1
  6. package/dist/assets/AiProfiles-CZMD4KM-.js +3 -0
  7. package/dist/assets/{BrokerEditorDialog-DPVRE7-s.js → BrokerEditorDialog-BCtpOmtb.js} +1 -1
  8. package/dist/assets/Build-BgRIdgDr.js +40 -0
  9. package/dist/assets/{Calendars-wYE1IQBj.js → Calendars-CvhWz72R.js} +1 -1
  10. package/dist/assets/Cluster-GaxjV7h4.js +1 -0
  11. package/dist/assets/{ComposedDeviceEditDialog-CymtiQhN.js → ComposedDeviceEditDialog-B1R5Dm6Q.js} +1 -1
  12. package/dist/assets/CompositionBlockEditor-CKIEoswd.js +2 -0
  13. package/dist/assets/{CompositionEditorPage-Bp0gwdfh.js → CompositionEditorPage-CZQd9oj4.js} +1 -1
  14. package/dist/assets/{Data-Cgf6SD1c.js → Data-CZij0oVZ.js} +2 -2
  15. package/dist/assets/DeviceDetail-DIqK-Pxm.js +2 -0
  16. package/dist/assets/{DeviceDoActionFields-BKIcUinN.js → DeviceDoActionFields-DAsmzytT.js} +1 -1
  17. package/dist/assets/{Devices-DtwfpOJe.js → Devices-60E3KVab.js} +1 -1
  18. package/dist/assets/{EmbedPlayerPage-C0JL6O13.js → EmbedPlayerPage-BCjssXwM.js} +1 -1
  19. package/dist/assets/{History-0nvSdtVT.js → History-QHsS9OSz.js} +1 -1
  20. package/dist/assets/{Identity-B9Ly0pEQ.js → Identity-Ck9bdqfT.js} +1 -1
  21. package/dist/assets/IntegrationDetail-Cggj8UuG.js +1 -0
  22. package/dist/assets/{IntegrationHub-BdTFw8jM.js → IntegrationHub-YFOb5Q5f.js} +1 -1
  23. package/dist/assets/{Integrations-DuajWOh1.js → Integrations-CAGCgM4m.js} +1 -1
  24. package/dist/assets/Integrations-CM_r2esc.js +1 -0
  25. package/dist/assets/Library-CDBh4UY7.js +1 -0
  26. package/dist/assets/{Locations-DbGCMvY_.js → Locations-C5J-xGpv.js} +2 -2
  27. package/dist/assets/{Logs-BsejI0N_.js → Logs-BzR8S8Az.js} +1 -1
  28. package/dist/assets/{MaskShapeCanvas-CCcjwlTm.js → MaskShapeCanvas-v1VFSWGn.js} +1 -1
  29. package/dist/assets/{MotionZonesSettings-15VWTACI.js → MotionZonesSettings-sNv6UeyN.js} +1 -1
  30. package/dist/assets/{MyAccess-BybIWHCc.js → MyAccess-d7f5aurQ.js} +1 -1
  31. package/dist/assets/{Network-DDhjZ0n2.js → Network-D5kLXsF4.js} +1 -1
  32. package/dist/assets/{NodeAddonsSettingsPanel-Cj9J3VCz.js → NodeAddonsSettingsPanel-Dul-TVez.js} +1 -1
  33. package/dist/assets/Notifications-BCXKW0H1.js +1 -0
  34. package/dist/assets/Pipeline-DQ0gg-q3.js +1 -0
  35. package/dist/assets/{PrivacyMaskSettings-boU8xnXR.js → PrivacyMaskSettings-BOFfKtZS.js} +1 -1
  36. package/dist/assets/{Recordings-BgaF6LHi.js → Recordings-CSEh6SKS.js} +1 -1
  37. package/dist/assets/{RemoteGrid-HppYsb5t.js → RemoteGrid-DwD_77yd.js} +1 -1
  38. package/dist/assets/{ScanNetworkModal-C44T3xR0.js → ScanNetworkModal-DfCSNZP7.js} +1 -1
  39. package/dist/assets/{SceneMonitorEditor-DbEDdw8n.js → SceneMonitorEditor-CYSd3nvA.js} +1 -1
  40. package/dist/assets/{SessionsList-DyX_VF8l.js → SessionsList-e-yTK3Ce.js} +2 -2
  41. package/dist/assets/{Showroom-B5qRvr4H.js → Showroom-BW0s66nO.js} +1 -1
  42. package/dist/assets/{System-DlY0oaGn.js → System-lEmnBarz.js} +1 -1
  43. package/dist/assets/{Templates-lCo5yN4g.js → Templates-oi3TGvJ_.js} +1 -1
  44. package/dist/assets/{Terminal-qRe7fRtd.js → Terminal-DSohGtV9.js} +2 -2
  45. package/dist/assets/{Viewer-BFtLOgb5.js → Viewer-B6FpTku6.js} +1 -1
  46. package/dist/assets/{__vite-browser-external-BlMfPwfH.js → __vite-browser-external-BCAtXlNQ.js} +1 -1
  47. package/dist/assets/{_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-Dd1lnVnV.js → _virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-DqPNLVRX.js} +1 -1
  48. package/dist/assets/{addon-icons-context-BqCF3aDM.js → addon-icons-context-BAMZN6v3.js} +1 -1
  49. package/dist/assets/{archive-COJvuz8j.js → archive-CQSA3no9.js} +1 -1
  50. package/dist/assets/{assignment-candidates-WjzzD3xR.js → assignment-candidates-CX47iOH5.js} +1 -1
  51. package/dist/assets/{authed-media-CPH4gDSb.js → authed-media-Bk6ZG2lV.js} +1 -1
  52. package/dist/assets/{bell-off-CEHvcpV8.js → bell-off-ByVFjmad.js} +1 -1
  53. package/dist/assets/{board-model-BkMxMFGJ.js → board-model-Cij0wyiz.js} +1 -1
  54. package/dist/assets/{boxes-C0Z7cB0S.js → boxes-B8shBGyA.js} +1 -1
  55. package/dist/assets/circle-slash-CYJP5692.js +1 -0
  56. package/dist/assets/client-B3Uh4h4a.js +674 -0
  57. package/dist/assets/{cloud-upload-yzPIJtWW.js → cloud-upload-knnOuFf0.js} +1 -1
  58. package/dist/assets/crop-CO-cCOHc.js +1 -0
  59. package/dist/assets/{device-integration-icon-Bo_gTwOD.js → device-integration-icon-CXZW9un_.js} +1 -1
  60. package/dist/assets/device-picker-model-CLwbjCLX.js +1 -0
  61. package/dist/assets/dist-2sWhwHC7.js +674 -0
  62. package/dist/assets/{dist-D7eSjbBx.js → dist-DWGbpqHP.js} +1 -1
  63. package/dist/assets/{file-up-gEH5fPam.js → file-up-DsJKDfNx.js} +1 -1
  64. package/dist/assets/{hard-drive-download-CwO_DU-K.js → hard-drive-download-3fltBasv.js} +1 -1
  65. package/dist/assets/{hostInit-BonUhoQT.js → hostInit-Djg3zVjo.js} +1 -1
  66. package/dist/assets/i18n-qDGVD_PF.js +1 -0
  67. package/dist/assets/icons-yMzKblPs.js +1 -0
  68. package/dist/assets/{index-i7JLUpAu.css → index-DrGgMc78.css} +1 -1
  69. package/dist/assets/index-Dvd8RKBK.js +4 -0
  70. package/dist/assets/{key-03tb4am1.js → key-DCYw0FNE.js} +1 -1
  71. package/dist/assets/{key-round-CxRbUD09.js → key-round-CTD-QhVl.js} +1 -1
  72. package/dist/assets/{layers-B_aq5GyC.js → layers-7Ihtoyz5.js} +1 -1
  73. package/dist/assets/{linked-devices-model-DO7_gXoE.js → linked-devices-model-n5gH_HKu.js} +1 -1
  74. package/dist/assets/{list-ordered-BIOnBH5V.js → list-ordered-oYfsQM3w.js} +1 -1
  75. package/dist/assets/location-owned-usage-CT1Figq_.js +1 -0
  76. package/dist/assets/{log-in-DYRRiyUu.js → log-in-BzAjDKMU.js} +1 -1
  77. package/dist/assets/{memory-stick-BTjX5XXU.js → memory-stick-XgJPnz3h.js} +1 -1
  78. package/dist/assets/{message-square-plus-CYHn2tx1.js → message-square-plus-DObXI5UO.js} +1 -1
  79. package/dist/assets/{mf-entry-bootstrap-0-2f0f55bc.js → mf-entry-bootstrap-0-8ecb92b3.js} +2 -2
  80. package/dist/assets/{mic-off-BfWqJn2R.js → mic-off-CFZpIFfk.js} +1 -1
  81. package/dist/assets/{monitor-off-Dy7WsqTN.js → monitor-off-610T_Gdj.js} +1 -1
  82. package/dist/assets/{monitor-smartphone-Cl-w7Gs0.js → monitor-smartphone-Dn0zJRqy.js} +1 -1
  83. package/dist/assets/mount-CLwbjCLX.js +1 -0
  84. package/dist/assets/player-overlays-CJHEGkWA.js +1 -0
  85. package/dist/assets/{recycle-CC6kU24N.js → recycle-BDhcl0YV.js} +1 -1
  86. package/dist/assets/remoteEntry-BkNPWH4W.js +1 -0
  87. package/dist/assets/{ruler-C6bIqLbX.js → ruler-DRBBjPda.js} +1 -1
  88. package/dist/assets/{scan-eye-BbYgoJiK.js → scan-eye-Cq1A21Jj.js} +1 -1
  89. package/dist/assets/{scissors-oYo-ZhBm.js → scissors-_YyUi2Wc.js} +1 -1
  90. package/dist/assets/{search-x-CySi2ygd.js → search-x-0cpMKp48.js} +1 -1
  91. package/dist/assets/{send-FMXSDirA.js → send-BPU5cRG9.js} +1 -1
  92. package/dist/assets/{settings-2-WfXnibr2.js → settings-2-L8TMaIKQ.js} +1 -1
  93. package/dist/assets/{shapes-JaoPTvAm.js → shapes-DJVtgjGZ.js} +1 -1
  94. package/dist/assets/{sigma-C2um2nX9.js → sigma-DPXOWC0b.js} +1 -1
  95. package/dist/assets/{speaker-DVh7yO4_.js → speaker-CokRNflr.js} +1 -1
  96. package/dist/assets/src-CzDAA2fz.js +67 -0
  97. package/dist/assets/{step-inspector-model-Xr3EG73i.js → step-inspector-model-8FwvV-vd.js} +1 -1
  98. package/dist/assets/{table-2-BzvZ1C8Y.js → table-2-DBJuUbJh.js} +1 -1
  99. package/dist/assets/{timer-reset-BFyZoIS7.js → timer-reset-D9k5wmF9.js} +1 -1
  100. package/dist/assets/{unlink-Bf6pcFmm.js → unlink-Bs2RzKEu.js} +1 -1
  101. package/dist/assets/{usb-oSHxCOV9.js → usb-BjhR3doW.js} +1 -1
  102. package/dist/assets/{use-scenes-feed-DrR6sBRO.js → use-scenes-feed-CT1HkH56.js} +1 -1
  103. package/dist/assets/{use-stationary-objects-DVmg7h4R.js → use-stationary-objects-HuzwOvOv.js} +1 -1
  104. package/dist/assets/useEventInvalidation-CLwbjCLX.js +1 -0
  105. package/dist/assets/{useNodeUrlSelection-CikIoR0h.js → useNodeUrlSelection-B82QsLnx.js} +1 -1
  106. package/dist/assets/user-round-reErwwaE.js +1 -0
  107. package/dist/assets/{user-x-aU8InWh0.js → user-x-lD1eMNGr.js} +1 -1
  108. package/dist/assets/{variable-DwONvC9h.js → variable-f5kvdTkN.js} +1 -1
  109. package/dist/assets/{video-DOy8UceJ.js → video-DNdx4dbt.js} +1 -1
  110. package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-DXQ8OkGD.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-BwnfAs-v.js} +2 -2
  111. package/dist/index.html +11 -11
  112. package/dist/server/addon.js +1282 -871
  113. package/package.json +1 -1
  114. package/dist/assets/Addons-BYYIoFw9.js +0 -5
  115. package/dist/assets/AiProfiles-Dp3jiABg.js +0 -3
  116. package/dist/assets/Build-C-tGTJ6v.js +0 -40
  117. package/dist/assets/Cluster-DaviTht0.js +0 -1
  118. package/dist/assets/CompositionBlockEditor-Bgk7i1Ug.js +0 -2
  119. package/dist/assets/DeviceDetail-B5bgKOno.js +0 -2
  120. package/dist/assets/IntegrationDetail-C7nPuXa_.js +0 -1
  121. package/dist/assets/Integrations-PJkA_opy.js +0 -1
  122. package/dist/assets/Library-fp_epw-B.js +0 -1
  123. package/dist/assets/Notifications-CxzR6VyG.js +0 -1
  124. package/dist/assets/Pipeline-DApdVNZ-.js +0 -1
  125. package/dist/assets/circle-slash-BY9zkWdJ.js +0 -1
  126. package/dist/assets/client-BjsFVjEa.js +0 -674
  127. package/dist/assets/crop-DpfML6TR.js +0 -1
  128. package/dist/assets/device-picker-model-C9XdDI0R.js +0 -1
  129. package/dist/assets/dist-DuYXDhcZ.js +0 -674
  130. package/dist/assets/eraser-CcAxRsoL.js +0 -1
  131. package/dist/assets/i18n-ZdV8UiYr.js +0 -1
  132. package/dist/assets/icons-l5aYjks5.js +0 -1
  133. package/dist/assets/index-CuSxNbC8.js +0 -4
  134. package/dist/assets/location-owned-usage-CYNROgPA.js +0 -1
  135. package/dist/assets/mount-C9XdDI0R.js +0 -1
  136. package/dist/assets/player-overlays-DaSesfVn.js +0 -1
  137. package/dist/assets/remoteEntry-C7WJfwvN.js +0 -1
  138. package/dist/assets/src-DxOL9qHw.js +0 -67
  139. package/dist/assets/useEventInvalidation-C9XdDI0R.js +0 -1
  140. package/dist/assets/user-round-BZHQXbgV.js +0 -1
@@ -5069,6 +5069,14 @@ function object(shape, params) {
5069
5069
  ...normalizeParams(params)
5070
5070
  });
5071
5071
  }
5072
+ function strictObject(shape, params) {
5073
+ return new ZodObject({
5074
+ type: "object",
5075
+ shape,
5076
+ catchall: never(),
5077
+ ...normalizeParams(params)
5078
+ });
5079
+ }
5072
5080
  function looseObject(shape, params) {
5073
5081
  return new ZodObject({
5074
5082
  type: "object",
@@ -5448,7 +5456,7 @@ var ZodIssueCode = {
5448
5456
  var ZodFirstPartyTypeKind;
5449
5457
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5450
5458
  //#endregion
5451
- //#region ../types/dist/sleep-DJws6ho7.mjs
5459
+ //#region ../types/dist/sleep-6eG9C8Xl.mjs
5452
5460
  /**
5453
5461
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5454
5462
  * window to float samples (D455).
@@ -8424,6 +8432,52 @@ function optionalMethod(input, output, options) {
8424
8432
  providerOptional: true
8425
8433
  };
8426
8434
  }
8435
+ /**
8436
+ * `addon-pages-source` — collection cap exposing per-provider raw page
8437
+ * declarations. Every addon that contributes a UI page registers a
8438
+ * provider here. The hub-side singleton aggregator (`addon-pages` cap,
8439
+ * see `addon-pages.cap.ts`) walks this collection, stamps versioned
8440
+ * `bundleUrl` values, and returns the enriched `AddonPageInfo[]` list
8441
+ * that admin-ui consumes.
8442
+ *
8443
+ * The split exists because the public listing has a different output
8444
+ * shape than the per-provider raw declarations, and we want both ends
8445
+ * to flow through codegen instead of relying on a hand-written wrapper.
8446
+ */
8447
+ var AddonPageDeclarationSchema = object({
8448
+ id: string(),
8449
+ label: string(),
8450
+ icon: string(),
8451
+ path: string(),
8452
+ /**
8453
+ * Module Federation remote name — must match the `name` field on the
8454
+ * page addon's `federation()` plugin config. Used by admin-ui's
8455
+ * `<AddonPageLoader>` to call `loadRemote('<remoteName>/page')`.
8456
+ * Conventionally `addon_<id>_page` (snake_case; MF names cannot
8457
+ * contain hyphens).
8458
+ */
8459
+ remoteName: string(),
8460
+ /**
8461
+ * Bundle filename inside the addon's `dist/` dir served at
8462
+ * `/api/addon-pages/<addonId>/<bundle>`. With Module Federation this
8463
+ * is always `'remoteEntry.js'`; the value is kept on the metadata so
8464
+ * the static-file route can compute an mtime-based cache-buster URL
8465
+ * without a separate filesystem stat.
8466
+ */
8467
+ bundle: string(),
8468
+ /**
8469
+ * Sidebar section this page docks into. Well-known ids: `'detection'`,
8470
+ * `'cluster'`, `'administration'` — the page renders inside that group.
8471
+ * Any OTHER string creates (or joins) a custom section rendered after
8472
+ * the built-in groups; its label comes from `sectionLabel` (first
8473
+ * declaration wins), falling back to the id. Absent → the legacy
8474
+ * "Addon Pages" group.
8475
+ */
8476
+ section: string().optional(),
8477
+ /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
8478
+ sectionLabel: string().optional()
8479
+ });
8480
+ method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
8427
8481
  var StaticDirOutputSchema$1 = object({ staticDir: string() });
8428
8482
  var VersionOutputSchema$1 = object({ version: string() });
8429
8483
  var adminUiCapability = {
@@ -8445,6 +8499,27 @@ var adminUiCapability = {
8445
8499
  getVersion: method(_void(), VersionOutputSchema$1, { auth: "admin" })
8446
8500
  }
8447
8501
  };
8502
+ /**
8503
+ * The pipeline step-input shape and the run-result bridge, in a module that
8504
+ * imports nothing but zod and types.
8505
+ *
8506
+ * WHY A SEPARATE FILE (addon footprint, D28): both were declared in
8507
+ * `pipeline-executor.cap.ts`, whose import evaluates the whole executor schema
8508
+ * graph. An addon that only needs these two (the benchmark's `runPipelineTest`
8509
+ * action) paid ~130 MB of heap at load. `pipeline-executor.cap.ts` re-exports
8510
+ * them, so every existing import keeps working; addon server code takes them
8511
+ * from `@camstack/types/addon`.
8512
+ */
8513
+ var PipelineStepInputSchema = lazy(() => object({
8514
+ addonId: string(),
8515
+ modelId: string().optional(),
8516
+ enabled: boolean().default(true),
8517
+ children: array(PipelineStepInputSchema).optional(),
8518
+ settings: record(string(), unknown()).optional(),
8519
+ jumpDeviceKey: string().optional()
8520
+ }));
8521
+ /** Typed bridge to the recursive `FrameResult` graph; validation is structural. */
8522
+ var PipelineRunResultBridge = custom();
8448
8523
  var StaticDirOutputSchema = object({ staticDir: string() });
8449
8524
  var VersionOutputSchema = object({ version: string() });
8450
8525
  method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
@@ -8629,6 +8704,7 @@ var CAP_NAMES = Object.freeze([
8629
8704
  "network-link",
8630
8705
  "network-quality",
8631
8706
  "node-certificates",
8707
+ "node-removal-hook",
8632
8708
  "nodes",
8633
8709
  "notification-output",
8634
8710
  "notification-rules",
@@ -8639,6 +8715,7 @@ var CAP_NAMES = Object.freeze([
8639
8715
  "osd",
8640
8716
  "osd-manager",
8641
8717
  "pet-feeder",
8718
+ "pet-fountain",
8642
8719
  "pipeline-analytics",
8643
8720
  "pipeline-executor",
8644
8721
  "pipeline-orchestrator",
@@ -9918,6 +9995,56 @@ Object.freeze({
9918
9995
  "valueType": "number"
9919
9996
  }
9920
9997
  ],
9998
+ "pet-fountain": [
9999
+ {
10000
+ "path": "waterLevel",
10001
+ "valueType": "number"
10002
+ },
10003
+ {
10004
+ "path": "waterVolume",
10005
+ "valueType": "number"
10006
+ },
10007
+ {
10008
+ "path": "empty",
10009
+ "valueType": "boolean"
10010
+ },
10011
+ {
10012
+ "path": "pumpRunning",
10013
+ "valueType": "boolean"
10014
+ },
10015
+ {
10016
+ "path": "pumpIntervalEnabled",
10017
+ "valueType": "boolean"
10018
+ },
10019
+ {
10020
+ "path": "pumpOnTime",
10021
+ "valueType": "number"
10022
+ },
10023
+ {
10024
+ "path": "pumpOffTime",
10025
+ "valueType": "number"
10026
+ },
10027
+ {
10028
+ "path": "lastDrink",
10029
+ "valueType": "number"
10030
+ },
10031
+ {
10032
+ "path": "currentHourDrink",
10033
+ "valueType": "number"
10034
+ },
10035
+ {
10036
+ "path": "last24hDrink",
10037
+ "valueType": "number"
10038
+ },
10039
+ {
10040
+ "path": "todayDrink",
10041
+ "valueType": "number"
10042
+ },
10043
+ {
10044
+ "path": "error",
10045
+ "valueType": "string"
10046
+ }
10047
+ ],
9921
10048
  "power-meter": [
9922
10049
  {
9923
10050
  "path": "power",
@@ -10455,7 +10582,7 @@ object({
10455
10582
  params: record(string(), union([string(), number()])).optional()
10456
10583
  });
10457
10584
  //#endregion
10458
- //#region ../types/dist/err-msg-BCdlR2Li.mjs
10585
+ //#region ../types/dist/err-msg-DWXWc0Di.mjs
10459
10586
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
10460
10587
  DeviceType["Camera"] = "camera";
10461
10588
  DeviceType["Hub"] = "hub";
@@ -10547,6 +10674,10 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
10547
10674
  * native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
10548
10675
  * integrations sharing the same food/desiccant/hopper surface. */
10549
10676
  DeviceType["PetFeeder"] = "pet-feeder";
10677
+ /** Pet water fountain — tank level, water drunk, pump and its duty cycle.
10678
+ * Installed with the `pet-fountain` cap. Reaches CamStack as loose entities
10679
+ * of a container and is BUILT as a composed device (D967). */
10680
+ DeviceType["PetFountain"] = "pet-fountain";
10550
10681
  /** Built-in dishwasher — program lifecycle, door / remote-start gates,
10551
10682
  * salt and rinse-aid levels. Installed with the `dishwasher` cap.
10552
10683
  * Sources: native Bosch Home Connect, reusable by similar integrations.
@@ -10807,7 +10938,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
10807
10938
  return DeviceRole;
10808
10939
  }({});
10809
10940
  //#endregion
10810
- //#region ../types/dist/composition-bIBW5_13.mjs
10941
+ //#region ../types/dist/composition-kSuOifuG.mjs
10811
10942
  /**
10812
10943
  * The ffmpeg build every CamStack node downloads, per platform and arch.
10813
10944
  *
@@ -11892,12 +12023,28 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
11892
12023
  ])
11893
12024
  })
11894
12025
  ]);
11895
- var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
12026
+ var forwardArgUnion = union([
12027
+ string(),
12028
+ number(),
12029
+ boolean(),
12030
+ strictObject({ ["$input"]: string().min(1) }),
12031
+ record(string().min(1), lazy(() => forwardArgUnion))
12032
+ ]);
12033
+ var CompositionForwardArgSchema = forwardArgUnion;
12034
+ /**
12035
+ * `forward` (D659, amended by D967): one method of a BUILT capability is one
12036
+ * call of `cap.method` on `source`. Without `args` the built method's input
12037
+ * passes through minus `deviceId`; with `args` the template IS the source's
12038
+ * whole input. `deviceId` is always the source's own, never an argument.
12039
+ */
12040
+ var CompositionForwardCommandSchema = object({
11896
12041
  kind: literal("forward"),
11897
12042
  source: CompositionSourceRefSchema,
11898
12043
  cap: string().min(1),
11899
- method: string().min(1)
11900
- }), CompositionCodeSourceSchema]);
12044
+ method: string().min(1),
12045
+ args: record(string().min(1), CompositionForwardArgSchema).optional()
12046
+ });
12047
+ var CompositionCommandTargetSchema = discriminatedUnion("kind", [CompositionForwardCommandSchema, CompositionCodeSourceSchema]);
11901
12048
  /** An item key inside an item-array cap (`consumables.items[<key>]`). */
11902
12049
  var COMPOSITION_ITEM_KEY_RE = /^[a-z0-9][a-z0-9-]{0,47}$/;
11903
12050
  /**
@@ -11907,7 +12054,13 @@ var COMPOSITION_ITEM_KEY_RE = /^[a-z0-9][a-z0-9-]{0,47}$/;
11907
12054
  */
11908
12055
  var CompositionItemEntrySchema = object({
11909
12056
  label: string().min(1).max(80),
11910
- fields: record(string().min(1), CompositionFieldSourceSchema)
12057
+ fields: record(string().min(1), CompositionFieldSourceSchema),
12058
+ /**
12059
+ * What the cap's `reset` does for THIS item (consumables). Present = the item
12060
+ * is resettable and a `reset` call naming its key forwards here. The item key
12061
+ * selects the command, so it is never an argument: there is no pass-through.
12062
+ */
12063
+ reset: CompositionForwardCommandSchema.optional()
11911
12064
  });
11912
12065
  var CompositionFeatureSchema = discriminatedUnion("kind", [object({
11913
12066
  kind: literal("fields"),
@@ -11919,7 +12072,7 @@ var CompositionFeatureSchema = discriminatedUnion("kind", [object({
11919
12072
  * owns the WHOLE array field: the composed value holds exactly these items.
11920
12073
  */
11921
12074
  items: record(string().regex(COMPOSITION_ITEM_KEY_RE), CompositionItemEntrySchema).refine((items) => Object.keys(items).length <= 16, { message: `at most 16 items` }).optional(),
11922
- /** RESERVED for slice 3; refused by the validator when non-empty. */
12075
+ /** Method name → what serves it. Only a BUILT capability takes commands (D967). */
11923
12076
  commands: record(string().min(1), CompositionCommandTargetSchema).optional()
11924
12077
  }), object({
11925
12078
  kind: literal("passthrough"),
@@ -15859,179 +16012,554 @@ object({
15859
16012
  lastError: string().nullable()
15860
16013
  });
15861
16014
  /**
15862
- * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
15863
- * surface that admin-ui consumes through `useAddonPagesListPages()`.
16015
+ * notification-output — canonical, capability-gated notification delivery.
15864
16016
  *
15865
- * The provider iterates every `addon-pages-source` (collection) provider
15866
- * and emits `AddonPageInfo[]` enriched with versioned `bundleUrl` strings
15867
- * pointing at `/api/addon-pages/<addonId>/<bundle>?v=<mtime>`. The
15868
- * filesystem `mtime` cache-buster lets the browser pick up addon
15869
- * rebuilds without manual reload.
16017
+ * Apprise-derived model (see
16018
+ * `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
16019
+ * callers emit ONE canonical `Notification`; each provider declares a
16020
+ * per-kind capability descriptor (`TargetKind`), and the pure degrade
16021
+ * engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
16022
+ * message to what the kind supports — callers never special-case a service.
15870
16023
  *
15871
- * The hub-local builtin `addon-pages-aggregator` (see
15872
- * `@camstack/system/builtins/addon-pages-aggregator`) registers the
15873
- * provider. Splitting the public aggregator from the raw collection
15874
- * keeps both ends in codegen — there's no hand-written
15875
- * `addon-pages.router.ts` wrapper anymore.
16024
+ * DESIGN DECISIONS (locked):
16025
+ * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
16026
+ * `setTargetEnabled`), each provider persisting via the `settings-store`
16027
+ * cap. Rationale: the admin UI needs one uniform surface across the
16028
+ * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
16029
+ * alternative would fork the UI per addon and cannot host the
16030
+ * discovery→adopt flow.
16031
+ * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
16032
+ * the generated cap-mount auto-`concatCollection`-fans them across every
16033
+ * registered provider (notifiers addon + HA addon) so one catalog is
16034
+ * routable. `send` / `testTarget` / CRUD route to ONE provider by the
16035
+ * `addonId` the generated collection router extracts from the call input.
16036
+ * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
16037
+ * transformer) + UDS MsgPack both round-trip typed arrays — already used by
16038
+ * `storage` / `storage-provider` / `recording` caps over the same path. No
16039
+ * base64 fallback needed.
16040
+ *
16041
+ * TODO (deferred, closed-set change — separate decision): add
16042
+ * `providerKind: 'notify'` so notification providers surface on the unified
16043
+ * admin "Integrations" page.
15876
16044
  */
15877
- var AddonPageDeclarationSchema$1 = object({
15878
- id: string(),
15879
- label: string(),
15880
- icon: string(),
15881
- path: string(),
15882
- remoteName: string(),
15883
- bundle: string(),
15884
- section: string().optional(),
15885
- sectionLabel: string().optional()
15886
- });
15887
- var AddonPageInfoSchema = object({
15888
- addonId: string(),
15889
- page: AddonPageDeclarationSchema$1,
15890
- bundleUrl: string()
15891
- });
15892
- method(_void(), array(AddonPageInfoSchema).readonly());
15893
16045
  /**
15894
- * `addon-pages-source` — collection cap exposing per-provider raw page
15895
- * declarations. Every addon that contributes a UI page registers a
15896
- * provider here. The hub-side singleton aggregator (`addon-pages` cap,
15897
- * see `addon-pages.cap.ts`) walks this collection, stamps versioned
15898
- * `bundleUrl` values, and returns the enriched `AddonPageInfo[]` list
15899
- * that admin-ui consumes.
16046
+ * Zentik-derived typed-media enum — the superset across every kind. Each
16047
+ * adapter picks what it supports and the degrade engine filters the rest.
16048
+ */
16049
+ var AttachmentMediaTypeSchema = _enum([
16050
+ "image",
16051
+ "video",
16052
+ "gif",
16053
+ "audio",
16054
+ "icon"
16055
+ ]);
16056
+ /**
16057
+ * A single attachment. Exactly one of `url` (remote source, most adapters
16058
+ * prefer this) or `bytes` (inline source; required for Pushover-style
16059
+ * bytes-only kinds) MUST be present — the degrade engine expresses a
16060
+ * url→bytes fetch as a `needsFetch` directive the adapter executes.
16061
+ */
16062
+ var AttachmentSchema = object({
16063
+ mediaType: AttachmentMediaTypeSchema,
16064
+ url: string().optional(),
16065
+ bytes: _instanceof(Uint8Array).optional(),
16066
+ mime: string().optional(),
16067
+ name: string().optional()
16068
+ }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
16069
+ var NotificationFormatSchema = _enum([
16070
+ "text",
16071
+ "markdown",
16072
+ "html"
16073
+ ]);
16074
+ /**
16075
+ * The CLOSED icon vocabulary an action button may use.
15900
16076
  *
15901
- * The split exists because the public listing has a different output
15902
- * shape than the per-provider raw declarations, and we want both ends
15903
- * to flow through codegen instead of relying on a hand-written wrapper.
16077
+ * A closed set, not a free string, and that is the whole point: an arbitrary
16078
+ * icon name is one that ntfy renders, zentik silently drops, and nobody
16079
+ * notices — the same class of gap as a zone vocabulary nothing produced
16080
+ * ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
16081
+ * declares `actionIcons: false` and the degrade engine strips the field.
16082
+ *
16083
+ * Named by INTENT, never by glyph. "check" would tie the vocabulary to one
16084
+ * renderer's icon set; "acknowledge" survives an adapter that draws it
16085
+ * differently.
16086
+ *
16087
+ * ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
16088
+ *
16089
+ * These names are for US. **No adapter may forward one verbatim.** Each maps
16090
+ * the whole set onto its own renderer's vocabulary through a
16091
+ * `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
16092
+ * fallback, so adding a member here fails every adapter's build until someone
16093
+ * decides its glyph, which is the only place that decision can be made
16094
+ * honestly.
16095
+ *
16096
+ * This paragraph is the bug. Zentik declared `actionIcons: true` and passed
16097
+ * `disarm` straight through; iOS feeds that string to
16098
+ * `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
16099
+ * and every snooze and alarm button arrived BLANK. A pass-through is not a
16100
+ * mapping, and "the field is documented" is not "the value renders".
16101
+ *
16102
+ * Adding a member is TRAIN-BOUND. The enum lives in the published
16103
+ * `@camstack/server` closure and the cap seam validates against the HUB's copy,
16104
+ * so an addon that emits a token the running hub does not know does not lose an
16105
+ * icon — its whole `send` fails Zod validation and the notification never
16106
+ * arrives. Never emit a new token from an addon before the train carrying it.
15904
16107
  */
15905
- var AddonPageDeclarationSchema = object({
16108
+ var NotificationActionIconSchema = _enum([
16109
+ "acknowledge",
16110
+ "dismiss",
16111
+ "silence",
16112
+ "snooze",
16113
+ "view",
16114
+ "play",
16115
+ "open",
16116
+ "close",
16117
+ "lock",
16118
+ "unlock",
16119
+ "arm",
16120
+ "arm-home",
16121
+ "arm-away",
16122
+ "arm-night",
16123
+ "disarm",
16124
+ "light",
16125
+ "alert",
16126
+ "camera"
16127
+ ]);
16128
+ /** A single tap-through action button. */
16129
+ var NotificationActionSchema = object({
15906
16130
  id: string(),
15907
16131
  label: string(),
15908
- icon: string(),
15909
- path: string(),
15910
- /**
15911
- * Module Federation remote name — must match the `name` field on the
15912
- * page addon's `federation()` plugin config. Used by admin-ui's
15913
- * `<AddonPageLoader>` to call `loadRemote('<remoteName>/page')`.
15914
- * Conventionally `addon_<id>_page` (snake_case; MF names cannot
15915
- * contain hyphens).
15916
- */
15917
- remoteName: string(),
16132
+ url: string().optional(),
16133
+ /** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
16134
+ icon: NotificationActionIconSchema.optional(),
15918
16135
  /**
15919
- * Bundle filename inside the addon's `dist/` dir served at
15920
- * `/api/addon-pages/<addonId>/<bundle>`. With Module Federation this
15921
- * is always `'remoteEntry.js'`; the value is kept on the metadata so
15922
- * the static-file route can compute an mtime-based cache-buster URL
15923
- * without a separate filesystem stat.
16136
+ * Renders in a warning style where the notifier supports it.
16137
+ *
16138
+ * A HINT, never a gate. The callback's authority is its token and nothing
16139
+ * else — see `notification-center/action-token.ts` for what that does and
16140
+ * does not buy.
15924
16141
  */
15925
- bundle: string(),
16142
+ destructive: boolean().optional(),
15926
16143
  /**
15927
- * Sidebar section this page docks into. Well-known ids: `'detection'`,
15928
- * `'cluster'`, `'administration'` — the page renders inside that group.
15929
- * Any OTHER string creates (or joins) a custom section rendered after
15930
- * the built-in groups; its label comes from `sectionLabel` (first
15931
- * declaration wins), falling back to the id. Absent → the legacy
15932
- * "Addon Pages" group.
16144
+ * How the tap should REACH the url.
16145
+ *
16146
+ * `navigate` (absent, and every button authored before this field) opens it:
16147
+ * the phone leaves the notification and shows whatever the callback returns.
16148
+ * That is right for a button whose answer the operator wants to read.
16149
+ *
16150
+ * `background` fires it as a POST and stays put. It exists for the buttons
16151
+ * whose whole point is not to interrupt — "silence this for 30 minutes" is
16152
+ * an answer to the notification, and being thrown into a browser tab to
16153
+ * confirm it costs more attention than the notification did. A backend that
16154
+ * cannot do a background call renders it as an ordinary link (the adapters
16155
+ * fall back rather than dropping the button), so this is a preference, never
16156
+ * a requirement.
15933
16157
  */
15934
- section: string().optional(),
15935
- /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
15936
- sectionLabel: string().optional()
16158
+ mode: _enum(["navigate", "background"]).optional()
15937
16159
  });
15938
- method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
15939
- var AddonHttpRouteSchema = object({
15940
- method: _enum([
15941
- "GET",
15942
- "POST",
15943
- "PUT",
15944
- "DELETE",
15945
- "PATCH"
16160
+ /** A delivery parameter value: what a notifier declared it takes. */
16161
+ var DeliveryParamValueSchema = union([
16162
+ string(),
16163
+ number(),
16164
+ boolean()
16165
+ ]);
16166
+ /**
16167
+ * One extra delivery parameter a notifier KIND declares (D938). A rule that
16168
+ * addresses a target of that kind may set it; a kind that declares nothing is
16169
+ * never handed one. The admin UI derives its control from this descriptor —
16170
+ * no editor knows a service by name.
16171
+ *
16172
+ * Optional fields are `.optional()`, never `.default()` (a Zod default does not
16173
+ * run on the addon cap path).
16174
+ */
16175
+ var DeliveryParamSchema = object({
16176
+ key: string().min(1),
16177
+ label: string(),
16178
+ /** i18n key of the label, resolved by the declaring addon's namespace. */
16179
+ labelKey: string().optional(),
16180
+ type: _enum([
16181
+ "enum",
16182
+ "number",
16183
+ "boolean",
16184
+ "string"
15946
16185
  ]),
15947
- path: string(),
15948
- access: _enum([
15949
- "public",
15950
- "authenticated",
15951
- "admin"
15952
- ]).optional(),
15953
- description: string().optional(),
15954
- /** The hub limits this route per client address before the handler runs. */
15955
- rateLimit: _enum(["auth"]).optional()
16186
+ /** `enum` only: the closed set of accepted values. */
16187
+ options: array(object({
16188
+ value: string(),
16189
+ label: string()
16190
+ })).optional(),
16191
+ /** `number` only. */
16192
+ min: number().optional(),
16193
+ max: number().optional(),
16194
+ /** What the notifier does when the rule sets nothing; shown as the placeholder. */
16195
+ default: DeliveryParamValueSchema.optional()
15956
16196
  });
15957
16197
  /**
15958
- * Cross-process route invocation envelope. The hub captures the
15959
- * request as plain data, ships it to the worker via Moleculer, and
15960
- * the worker runs the local handler against a capturing reply. The
15961
- * envelope returned describes what the handler intended (status,
15962
- * headers, body, or a redirect) so the hub can translate it back to
15963
- * the Fastify reply that's actually wired to the socket.
16198
+ * The canonical notification. `body` is the only hard field (Apprise model).
16199
+ * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
16200
+ * NOT a fixed severity enum — each kind declares its own `caps.levels` and
16201
+ * the adapter maps this ordinal onto its native level. `level?` is an
16202
+ * optional kind-native level id (`emergency`, `silent`, …) that overrides
16203
+ * `priority` for that one target.
15964
16204
  */
15965
- var InvokeRequestSchema = object({
15966
- method: string(),
15967
- path: string(),
15968
- params: record(string(), string()),
15969
- query: record(string(), string()),
15970
- body: unknown(),
15971
- headers: record(string(), string()),
15972
- user: object({
15973
- id: string(),
15974
- username: string(),
15975
- isAdmin: boolean()
15976
- }).optional(),
15977
- scopedToken: unknown().optional()
15978
- });
15979
- var InvokeReplyEnvelopeSchema = object({
15980
- status: number().int(),
15981
- headers: record(string(), string()),
15982
- /** When set, the hub MUST `reply.redirect(redirectUrl)` instead of
15983
- * sending `body`. Status defaults to 302 when this is set unless
15984
- * the handler called `reply.code(...)` explicitly. */
15985
- redirectUrl: string().nullable(),
15986
- /** JSON-serializable body. `undefined` is treated as "no body". */
15987
- body: unknown().optional(),
15988
- /** Set when the handler called `reply.type(mime)`. */
15989
- contentType: string().optional()
16205
+ var NotificationSchema = object({
16206
+ body: string(),
16207
+ title: string().optional(),
16208
+ format: NotificationFormatSchema.default("text"),
16209
+ priority: number().int().min(1).max(5).default(3),
16210
+ level: string().optional(),
16211
+ attachments: array(AttachmentSchema).optional(),
16212
+ clickUrl: string().optional(),
16213
+ actions: array(NotificationActionSchema).optional(),
16214
+ sound: string().optional(),
16215
+ ttl: number().optional(),
16216
+ tag: string().optional(),
16217
+ deviceId: number().optional(),
16218
+ eventId: string().optional(),
16219
+ metadata: record(string(), unknown()).optional(),
16220
+ /**
16221
+ * Values for the parameters the target's kind DECLARES in
16222
+ * {@link TargetKindSchema} `deliveryParams`. Already validated against the
16223
+ * declaration by the sender; a notifier reads only the keys it declared.
16224
+ */
16225
+ delivery: record(string(), DeliveryParamValueSchema).optional()
15990
16226
  });
15991
- method(_void(), array(AddonHttpRouteSchema)), method(InvokeRequestSchema, InvokeReplyEnvelopeSchema, { kind: "mutation" });
15992
- var ConfigTabDeclarationSchema = object({
16227
+ /** One declared native severity/priority level for a kind. */
16228
+ var TargetKindLevelSchema = object({
15993
16229
  id: string(),
15994
16230
  label: string(),
15995
- icon: string(),
15996
- order: number().optional()
15997
- });
15998
- var ConfigSectionWithValuesSchema = object({
15999
- id: string(),
16000
- title: string(),
16001
- titleKey: string().optional(),
16002
- description: string().optional(),
16003
- descriptionKey: string().optional(),
16004
- style: _enum(["card", "accordion"]).optional(),
16005
- defaultCollapsed: boolean().optional(),
16006
- columns: union([
16007
- literal(1),
16008
- literal(2),
16009
- literal(3),
16010
- literal(4)
16011
- ]).optional(),
16012
- tab: string().optional(),
16013
- location: _enum([
16014
- "settings",
16015
- "top-tab",
16016
- "hero"
16017
- ]).optional(),
16018
- order: number().optional(),
16019
- fields: array(any())
16231
+ /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
16232
+ ordinal: number().int().min(1).max(5).nullable(),
16233
+ flags: object({
16234
+ critical: boolean().optional(),
16235
+ silent: boolean().optional(),
16236
+ noPush: boolean().optional()
16237
+ }).optional(),
16238
+ /** e.g. Pushover `emergency` requires `retry` / `expire`. */
16239
+ requires: array(string()).optional(),
16240
+ description: string().optional()
16020
16241
  });
16021
- var SettingsSchemaWithValuesSchema = object({
16022
- tabs: array(ConfigTabDeclarationSchema).optional(),
16023
- sections: array(ConfigSectionWithValuesSchema)
16242
+ /** The full capability block consulted before dispatch. */
16243
+ var TargetKindCapsSchema = object({
16244
+ attachments: object({
16245
+ mediaTypes: array(AttachmentMediaTypeSchema),
16246
+ mode: _enum([
16247
+ "url",
16248
+ "bytes",
16249
+ "both"
16250
+ ]),
16251
+ max: number().int().nonnegative(),
16252
+ maxBytes: number().int().positive().optional()
16253
+ }),
16254
+ /** Max action buttons (0 = none). */
16255
+ actions: number().int().nonnegative(),
16256
+ /**
16257
+ * Whether this kind renders a per-action ICON.
16258
+ *
16259
+ * `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
16260
+ * run on the addon cap path — three production failures in one day taught
16261
+ * this repo that once. Absent is read as false by the degrade engine, which
16262
+ * is the safe direction: an icon that is not rendered costs nothing, an icon
16263
+ * assumed and dropped costs the operator's trust in the field.
16264
+ */
16265
+ actionIcons: boolean().optional(),
16266
+ levels: array(TargetKindLevelSchema),
16267
+ format: array(NotificationFormatSchema),
16268
+ clickUrl: boolean(),
16269
+ sound: boolean(),
16270
+ ttl: boolean(),
16271
+ bodyMaxLen: number().int().positive()
16024
16272
  });
16025
- /** Patch object — keys are field names, values are the new field values. */
16026
- var SettingsPatchSchema = record(string(), unknown());
16027
- /** Standard success response for update operations. */
16028
- var SettingsUpdateResultSchema = object({ success: literal(true) });
16029
- method(object({
16030
- addonId: string(),
16031
- nodeId: string().optional(),
16032
- overlay: record(string(), unknown()).optional(),
16033
- cap: string().optional()
16034
- }), SettingsSchemaWithValuesSchema.nullable()), method(object({
16273
+ /**
16274
+ * `configSchema` is a `ConfigUISchema` tree passed through to the admin
16275
+ * FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
16276
+ * `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
16277
+ * the union is large and not meant for runtime validation here; the exported
16278
+ * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
16279
+ */
16280
+ var ConfigSchemaPassthrough$3 = unknown();
16281
+ var TargetKindSchema = object({
16282
+ kind: string(),
16283
+ label: string(),
16284
+ icon: string(),
16285
+ /** Stamped by each provider so the concat-fanned catalog stays routable. */
16286
+ addonId: string(),
16287
+ /**
16288
+ * URL of the kind's bundled BRAND icon, served by the providing addon over
16289
+ * its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
16290
+ * when the addon bundles no icon for that kind — the client then falls back
16291
+ * to a neutral glyph rather than rendering the raw `icon` NAME as text.
16292
+ *
16293
+ * Root-relative on purpose: it resolves against whatever origin serves a web
16294
+ * client, and a native client joins it onto its own hub base.
16295
+ *
16296
+ * DECLARED here deliberately. It used to travel as an undeclared passthrough
16297
+ * field that survived only because the runtime cap-router forwards provider
16298
+ * output verbatim — so every consumer had to re-declare it by hand to stop
16299
+ * its own Zod parse from stripping it, and the whole arrangement would have
16300
+ * broken silently the moment output validation was tightened anywhere.
16301
+ */
16302
+ iconUrl: string().optional(),
16303
+ /**
16304
+ * Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
16305
+ *
16306
+ * The server knows this and therefore says it, because the client cannot
16307
+ * safely guess: a React-Native client renders SVG and raster through two
16308
+ * DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
16309
+ * not decode SVG on iOS/Android), so without this it silently fell back to a
16310
+ * placeholder glyph for every vector icon while the web build looked fine.
16311
+ *
16312
+ * Absent when {@link iconUrl} is absent, or for a legacy provider that has
16313
+ * not been updated — a client that cannot determine the type should prefer
16314
+ * its raster path, which is the safe default for an unknown image.
16315
+ */
16316
+ iconMediaType: string().optional(),
16317
+ configSchema: ConfigSchemaPassthrough$3,
16318
+ /**
16319
+ * The config keys that decide WHERE this kind's secrets are sent (server,
16320
+ * base URL, instance, chat, recipient). A save that changes any of them
16321
+ * while leaving a secret redacted is refused by the provider (D634), so an
16322
+ * editor uses this to ask for the secret on exactly those edits instead of
16323
+ * on every edit.
16324
+ *
16325
+ * Optional: absent from a provider that holds no secret (the Home Assistant
16326
+ * kinds) and from a provider older than this field — an editor that finds
16327
+ * it absent must assume ANY edit may need the secret.
16328
+ */
16329
+ destinationKeys: array(string()).optional(),
16330
+ supportsDiscovery: boolean(),
16331
+ /**
16332
+ * Extra delivery parameters a rule may set for a target of this kind (D938).
16333
+ * Absent = none; the rule editor then offers nothing.
16334
+ */
16335
+ deliveryParams: array(DeliveryParamSchema).optional(),
16336
+ caps: TargetKindCapsSchema
16337
+ });
16338
+ /**
16339
+ * A persisted target. `config` holds secrets; providers REDACT secret fields
16340
+ * (return a presence marker only) when serving `listTargets` — never
16341
+ * round-trip a stored secret to the UI.
16342
+ */
16343
+ var TargetSchema = object({
16344
+ id: string(),
16345
+ name: string(),
16346
+ kind: string(),
16347
+ addonId: string(),
16348
+ enabled: boolean(),
16349
+ config: record(string(), unknown())
16350
+ });
16351
+ /** A discovery-surfaced candidate (config is partial + non-secret). */
16352
+ var DiscoveredTargetSchema = object({
16353
+ kind: string(),
16354
+ suggestedName: string(),
16355
+ config: record(string(), unknown())
16356
+ });
16357
+ /** The degrade engine's report — what was resolved / dropped / degraded. */
16358
+ var RenderedAsSchema = object({
16359
+ level: string(),
16360
+ format: NotificationFormatSchema,
16361
+ attachmentsSent: number().int().nonnegative(),
16362
+ actionsSent: number().int().nonnegative(),
16363
+ truncated: boolean(),
16364
+ dropped: array(string())
16365
+ });
16366
+ var SendResultSchema = object({
16367
+ success: boolean(),
16368
+ error: string().optional(),
16369
+ renderedAs: RenderedAsSchema.optional()
16370
+ });
16371
+ /** Same shape as SendResult — kept as a distinct name for the test panel. */
16372
+ var TestResultSchema = SendResultSchema;
16373
+ /**
16374
+ * Pre-save credential probe against a partial (unsaved) target config.
16375
+ * `resolvedUrl` never includes secrets (token stripped from path).
16376
+ */
16377
+ var ProbeTargetConfigResultSchema = object({
16378
+ ok: boolean(),
16379
+ resolvedUrl: string(),
16380
+ httpStatus: number().int().optional(),
16381
+ stateInstance: string().optional(),
16382
+ error: string().optional()
16383
+ });
16384
+ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema), { auth: "admin" }), method(object({ targetId: string() }), TargetSchema, { auth: "admin" }), method(object({
16385
+ kind: string(),
16386
+ config: record(string(), unknown()).optional(),
16387
+ /**
16388
+ * When editing / duplicating, the blank password field never round-trips
16389
+ * the stored token. Pass the source target id so the provider can restore
16390
+ * secrets server-side before dialling (same merge as upsert).
16391
+ */
16392
+ targetId: string().optional()
16393
+ }), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
16394
+ kind: string(),
16395
+ config: record(string(), unknown()),
16396
+ /** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
16397
+ targetId: string().optional()
16398
+ }), ProbeTargetConfigResultSchema, {
16399
+ kind: "mutation",
16400
+ auth: "admin"
16401
+ }), method(object({
16402
+ targetId: string(),
16403
+ notification: NotificationSchema
16404
+ }), SendResultSchema, {
16405
+ kind: "mutation",
16406
+ auth: "admin"
16407
+ }), method(object({
16408
+ targetId: string(),
16409
+ sample: NotificationSchema.optional()
16410
+ }), TestResultSchema, {
16411
+ kind: "mutation",
16412
+ auth: "admin"
16413
+ }), method(object({
16414
+ target: TargetSchema,
16415
+ /**
16416
+ * Duplicate flow: create a NEW target id while copying secrets from an
16417
+ * existing row. The UI never holds the stored token; blank / redacted
16418
+ * password fields are restored from this source (D634 still applies if
16419
+ * destinations diverge without a re-typed secret).
16420
+ */
16421
+ cloneSecretsFrom: string().optional()
16422
+ }), TargetSchema, {
16423
+ kind: "mutation",
16424
+ auth: "admin"
16425
+ }), method(object({ targetId: string() }), _void(), {
16426
+ kind: "mutation",
16427
+ auth: "admin"
16428
+ }), method(object({
16429
+ targetId: string(),
16430
+ enabled: boolean()
16431
+ }), _void(), {
16432
+ kind: "mutation",
16433
+ auth: "admin"
16434
+ });
16435
+ /**
16436
+ * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16437
+ * surface that admin-ui consumes through `useAddonPagesListPages()`.
16438
+ *
16439
+ * The provider iterates every `addon-pages-source` (collection) provider
16440
+ * and emits `AddonPageInfo[]` enriched with versioned `bundleUrl` strings
16441
+ * pointing at `/api/addon-pages/<addonId>/<bundle>?v=<mtime>`. The
16442
+ * filesystem `mtime` cache-buster lets the browser pick up addon
16443
+ * rebuilds without manual reload.
16444
+ *
16445
+ * The hub-local builtin `addon-pages-aggregator` (see
16446
+ * `@camstack/system/builtins/addon-pages-aggregator`) registers the
16447
+ * provider. Splitting the public aggregator from the raw collection
16448
+ * keeps both ends in codegen — there's no hand-written
16449
+ * `addon-pages.router.ts` wrapper anymore.
16450
+ */
16451
+ var AddonPageDeclarationSchema$1 = object({
16452
+ id: string(),
16453
+ label: string(),
16454
+ icon: string(),
16455
+ path: string(),
16456
+ remoteName: string(),
16457
+ bundle: string(),
16458
+ section: string().optional(),
16459
+ sectionLabel: string().optional()
16460
+ });
16461
+ var AddonPageInfoSchema = object({
16462
+ addonId: string(),
16463
+ page: AddonPageDeclarationSchema$1,
16464
+ bundleUrl: string()
16465
+ });
16466
+ method(_void(), array(AddonPageInfoSchema).readonly());
16467
+ var AddonHttpRouteSchema = object({
16468
+ method: _enum([
16469
+ "GET",
16470
+ "POST",
16471
+ "PUT",
16472
+ "DELETE",
16473
+ "PATCH"
16474
+ ]),
16475
+ path: string(),
16476
+ access: _enum([
16477
+ "public",
16478
+ "authenticated",
16479
+ "admin"
16480
+ ]).optional(),
16481
+ description: string().optional(),
16482
+ /** The hub limits this route per client address before the handler runs. */
16483
+ rateLimit: _enum(["auth"]).optional()
16484
+ });
16485
+ /**
16486
+ * Cross-process route invocation envelope. The hub captures the
16487
+ * request as plain data, ships it to the worker via Moleculer, and
16488
+ * the worker runs the local handler against a capturing reply. The
16489
+ * envelope returned describes what the handler intended (status,
16490
+ * headers, body, or a redirect) so the hub can translate it back to
16491
+ * the Fastify reply that's actually wired to the socket.
16492
+ */
16493
+ var InvokeRequestSchema = object({
16494
+ method: string(),
16495
+ path: string(),
16496
+ params: record(string(), string()),
16497
+ query: record(string(), string()),
16498
+ body: unknown(),
16499
+ headers: record(string(), string()),
16500
+ user: object({
16501
+ id: string(),
16502
+ username: string(),
16503
+ isAdmin: boolean()
16504
+ }).optional(),
16505
+ scopedToken: unknown().optional()
16506
+ });
16507
+ var InvokeReplyEnvelopeSchema = object({
16508
+ status: number().int(),
16509
+ headers: record(string(), string()),
16510
+ /** When set, the hub MUST `reply.redirect(redirectUrl)` instead of
16511
+ * sending `body`. Status defaults to 302 when this is set unless
16512
+ * the handler called `reply.code(...)` explicitly. */
16513
+ redirectUrl: string().nullable(),
16514
+ /** JSON-serializable body. `undefined` is treated as "no body". */
16515
+ body: unknown().optional(),
16516
+ /** Set when the handler called `reply.type(mime)`. */
16517
+ contentType: string().optional()
16518
+ });
16519
+ method(_void(), array(AddonHttpRouteSchema)), method(InvokeRequestSchema, InvokeReplyEnvelopeSchema, { kind: "mutation" });
16520
+ var ConfigTabDeclarationSchema = object({
16521
+ id: string(),
16522
+ label: string(),
16523
+ icon: string(),
16524
+ order: number().optional()
16525
+ });
16526
+ var ConfigSectionWithValuesSchema = object({
16527
+ id: string(),
16528
+ title: string(),
16529
+ titleKey: string().optional(),
16530
+ description: string().optional(),
16531
+ descriptionKey: string().optional(),
16532
+ style: _enum(["card", "accordion"]).optional(),
16533
+ defaultCollapsed: boolean().optional(),
16534
+ columns: union([
16535
+ literal(1),
16536
+ literal(2),
16537
+ literal(3),
16538
+ literal(4)
16539
+ ]).optional(),
16540
+ tab: string().optional(),
16541
+ location: _enum([
16542
+ "settings",
16543
+ "top-tab",
16544
+ "hero"
16545
+ ]).optional(),
16546
+ order: number().optional(),
16547
+ fields: array(any())
16548
+ });
16549
+ var SettingsSchemaWithValuesSchema = object({
16550
+ tabs: array(ConfigTabDeclarationSchema).optional(),
16551
+ sections: array(ConfigSectionWithValuesSchema)
16552
+ });
16553
+ /** Patch object — keys are field names, values are the new field values. */
16554
+ var SettingsPatchSchema = record(string(), unknown());
16555
+ /** Standard success response for update operations. */
16556
+ var SettingsUpdateResultSchema = object({ success: literal(true) });
16557
+ method(object({
16558
+ addonId: string(),
16559
+ nodeId: string().optional(),
16560
+ overlay: record(string(), unknown()).optional(),
16561
+ cap: string().optional()
16562
+ }), SettingsSchemaWithValuesSchema.nullable()), method(object({
16035
16563
  addonId: string(),
16036
16564
  nodeId: string().optional(),
16037
16565
  patch: SettingsPatchSchema
@@ -18478,7 +19006,11 @@ var CompositionProblemSchema = object({
18478
19006
  "mixed-quantity",
18479
19007
  "helper-mismatch",
18480
19008
  "member-constraint",
18481
- "retention"
19009
+ "retention",
19010
+ "command-method-unknown",
19011
+ "command-source-unknown",
19012
+ "command-input-unknown",
19013
+ "command-input-missing"
18482
19014
  ]),
18483
19015
  path: string(),
18484
19016
  message: string()
@@ -18517,7 +19049,9 @@ var CompositionFieldRoleSchema = _enum([
18517
19049
  var CompositionFieldItemSchema = object({
18518
19050
  arrayPath: string(),
18519
19051
  key: string(),
18520
- label: string()
19052
+ label: string(),
19053
+ /** True when the item carries a `reset` forward: the composer writes `resettable` from this (D967). */
19054
+ resettable: boolean().optional()
18521
19055
  });
18522
19056
  var CompositionFieldPlanSchema = object({
18523
19057
  cap: string(),
@@ -18722,7 +19256,7 @@ Object.freeze({
18722
19256
  * One catalog row per base reading a group helper can build (D944 §3).
18723
19257
  * Data, not a form default written into the block.
18724
19258
  */
18725
- var NOW$2 = {
19259
+ var NOW$3 = {
18726
19260
  fieldPath: "lastFetchedAt",
18727
19261
  expr: "now"
18728
19262
  };
@@ -18774,7 +19308,7 @@ var GROUP_TEMPLATES = [
18774
19308
  quantity: "temperature",
18775
19309
  defaultAggregate: "mean",
18776
19310
  empty: "null",
18777
- companions: [NOW$2],
19311
+ companions: [NOW$3],
18778
19312
  alsoFrom: {
18779
19313
  deviceTypes: CLIMATE_DEVICES,
18780
19314
  accept: {
@@ -18792,7 +19326,7 @@ var GROUP_TEMPLATES = [
18792
19326
  quantity: "humidity",
18793
19327
  defaultAggregate: "mean",
18794
19328
  empty: "null",
18795
- companions: [NOW$2],
19329
+ companions: [NOW$3],
18796
19330
  alsoFrom: {
18797
19331
  deviceTypes: CLIMATE_DEVICES,
18798
19332
  accept: {
@@ -18810,7 +19344,7 @@ var GROUP_TEMPLATES = [
18810
19344
  quantity: "pressure",
18811
19345
  defaultAggregate: "mean",
18812
19346
  empty: "null",
18813
- companions: [NOW$2]
19347
+ companions: [NOW$3]
18814
19348
  }),
18815
19349
  numeric({
18816
19350
  id: "illuminance",
@@ -18821,7 +19355,7 @@ var GROUP_TEMPLATES = [
18821
19355
  quantity: "illuminance",
18822
19356
  defaultAggregate: "mean",
18823
19357
  empty: "null",
18824
- companions: [NOW$2]
19358
+ companions: [NOW$3]
18825
19359
  }),
18826
19360
  numeric({
18827
19361
  id: "power",
@@ -18832,7 +19366,7 @@ var GROUP_TEMPLATES = [
18832
19366
  quantity: "power",
18833
19367
  defaultAggregate: "sum",
18834
19368
  empty: "omit",
18835
- companions: [NOW$2]
19369
+ companions: [NOW$3]
18836
19370
  }),
18837
19371
  numeric({
18838
19372
  id: "energy",
@@ -18843,7 +19377,7 @@ var GROUP_TEMPLATES = [
18843
19377
  quantity: "energy",
18844
19378
  defaultAggregate: "sum",
18845
19379
  empty: "omit",
18846
- companions: [NOW$2]
19380
+ companions: [NOW$3]
18847
19381
  }),
18848
19382
  numeric({
18849
19383
  id: "battery",
@@ -18878,7 +19412,7 @@ var GROUP_TEMPLATES = [
18878
19412
  quantity: "device",
18879
19413
  defaultAggregate: "mean",
18880
19414
  empty: "null",
18881
- companions: [NOW$2]
19415
+ companions: [NOW$3]
18882
19416
  }),
18883
19417
  binary({
18884
19418
  id: "motion",
@@ -21224,12 +21758,48 @@ var ExposeInputSchema = object({
21224
21758
  capabilities: array(string()).optional()
21225
21759
  });
21226
21760
  var UnexposeInputSchema = object({ deviceId: string() });
21761
+ /**
21762
+ * What an exporter may expose besides devices (D968). An action and a
21763
+ * notification target are LIST ENTRIES, never devices (D954): they enter an
21764
+ * exporter's own selection, not the device registry.
21765
+ */
21766
+ var ExportEntityKindSchema = _enum([
21767
+ "device",
21768
+ "action",
21769
+ "notifier"
21770
+ ]);
21771
+ var ExportEntityRefSchema = discriminatedUnion("kind", [
21772
+ object({
21773
+ kind: literal("device"),
21774
+ deviceId: string()
21775
+ }),
21776
+ object({
21777
+ kind: literal("action"),
21778
+ actionId: string().min(1)
21779
+ }),
21780
+ object({
21781
+ kind: literal("notifier"),
21782
+ targetId: string().min(1)
21783
+ })
21784
+ ]);
21785
+ var ExposedEntitySchema = object({
21786
+ entity: ExportEntityRefSchema,
21787
+ exposedAs: string().optional()
21788
+ });
21789
+ var ExposeEntityInputSchema = object({ entity: ExportEntityRefSchema });
21790
+ var UnexposeEntityInputSchema = object({ entity: ExportEntityRefSchema });
21227
21791
  method(_void(), DeviceExportStatusSchema), method(_void(), array(DeviceKindSchema)), method(_void(), array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
21228
21792
  kind: "mutation",
21229
21793
  auth: "admin"
21230
21794
  }), method(UnexposeInputSchema, _void(), {
21231
21795
  kind: "mutation",
21232
21796
  auth: "admin"
21797
+ }), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(_void(), array(ExposedEntitySchema)), optionalMethod(ExposeEntityInputSchema, _void(), {
21798
+ kind: "mutation",
21799
+ auth: "admin"
21800
+ }), optionalMethod(UnexposeEntityInputSchema, _void(), {
21801
+ kind: "mutation",
21802
+ auth: "admin"
21233
21803
  });
21234
21804
  /** Lucide category ids, alphabetical. `ICON_META` indexes into this. */
21235
21805
  var ICON_CATEGORY_IDS = [
@@ -42370,6 +42940,14 @@ method(object({
42370
42940
  kind: "mutation",
42371
42941
  auth: "admin"
42372
42942
  });
42943
+ method(object({
42944
+ nodeId: string().min(1),
42945
+ /** Epoch ms the node was forgotten (the tombstone's `removedAt`). */
42946
+ removedAt: number()
42947
+ }), object({ counts: record(string(), number().int().nonnegative()) }), {
42948
+ kind: "mutation",
42949
+ auth: "admin"
42950
+ });
42373
42951
  /**
42374
42952
  * `device-removal-hook` — the reliable way an addon learns a device was DELETED,
42375
42953
  * so it can delete what it keeps for that device (D865).
@@ -43090,7 +43668,7 @@ var LlmProviderWidgetSchema = object({
43090
43668
  /** ConfigUISchema tree passed through untyped on the wire (the
43091
43669
  * notification-output `ConfigSchemaPassthrough` precedent at
43092
43670
  * notification-output.cap.ts:151); the exported TS type re-tightens it. */
43093
- var ConfigSchemaPassthrough$3 = unknown();
43671
+ var ConfigSchemaPassthrough$2 = unknown();
43094
43672
  var LlmProfileKindDescriptorSchema = object({
43095
43673
  kind: LlmProfileKindSchema,
43096
43674
  label: string(),
@@ -43099,9 +43677,9 @@ var LlmProfileKindDescriptorSchema = object({
43099
43677
  /** Stamped by each provider so the concat-fanned catalog stays routable. */
43100
43678
  addonId: string(),
43101
43679
  /** The provider's own fields (`providerConfig`); `password` fields are keychain-held. */
43102
- connectionSchema: ConfigSchemaPassthrough$3,
43680
+ connectionSchema: ConfigSchemaPassthrough$2,
43103
43681
  /** Provider fields that belong under "Advanced" (extra headers, runtime tuning). */
43104
- advancedSchema: ConfigSchemaPassthrough$3.optional(),
43682
+ advancedSchema: ConfigSchemaPassthrough$2.optional(),
43105
43683
  /** The kind's defaults; a model from `listModels` may override any of them. */
43106
43684
  features: LlmModelFeaturesSchema,
43107
43685
  /** `false` = the provider stamps `supportsVision` itself (from what a node reports). */
@@ -43627,10 +44205,10 @@ var CpuBreakdownSchema = object({
43627
44205
  cores: number()
43628
44206
  });
43629
44207
  var MemoryInfoSchema = object({
43630
- percent: number(),
44208
+ percent: number().nullable(),
43631
44209
  totalBytes: number(),
43632
- usedBytes: number(),
43633
- availableBytes: number(),
44210
+ usedBytes: number().nullable(),
44211
+ availableBytes: number().nullable(),
43634
44212
  swapUsedBytes: number(),
43635
44213
  swapTotalBytes: number()
43636
44214
  });
@@ -43900,698 +44478,278 @@ var NodeLoadSeriesSchema = object({
43900
44478
  /**
43901
44479
  * Did the DURABLE tier contribute? `false` means the answer is the hot ring
43902
44480
  * alone — an agent (which holds no table), or a store that refused.
43903
- * Reported because "the last hour" and "the last six hours" are different
43904
- * questions and an operator must not have to guess which was answered.
43905
- */
43906
- durable: boolean()
43907
- });
43908
- var GetLoadSeriesInputSchema = object({
43909
- /**
43910
- * The node whose series is wanted.
43911
- *
43912
- * NOT named `nodeId`: the generated cap router strips a top-level
43913
- * `nodeId` from every method input and uses it to ROUTE the call to
43914
- * that node's provider (`generated-cap-routers.ts`). A series target
43915
- * called `nodeId` would silently become a routing pin and never reach
43916
- * the provider. The hub holds every node it hears from, so the
43917
- * ordinary call is unpinned — answered by the hub, for any node.
43918
- */
43919
- forNodeId: string(),
43920
- /**
43921
- * EXCLUSIVE lower bound. A caller passes the newest `atMs` it already
43922
- * holds and receives only what it is missing, so seeding a live chart
43923
- * from this method cannot double a point already drawn.
43924
- */
43925
- sinceMs: number().optional(),
43926
- /**
43927
- * Most points the caller wants PER FUNCTION. The window is reduced to fit,
43928
- * preserving min and max per bucket.
43929
- *
43930
- * Absent means NO reduction — legitimate for a short window and a trap for a
43931
- * long one, which is why a chart passes its own pixel width.
43932
- */
43933
- maxPoints: number().int().positive().optional()
43934
- });
43935
- var SystemMetricsSchema = object({
43936
- cpuPercent: number(),
43937
- memoryPercent: number(),
43938
- memoryUsedMB: number(),
43939
- memoryTotalMB: number(),
43940
- diskPercent: number().optional(),
43941
- temperature: number().optional(),
43942
- gpuPercent: number().optional(),
43943
- gpuMemoryPercent: number().optional()
43944
- });
43945
- method(_void(), SystemResourceSnapshotSchema), method(_void(), SystemResourceSnapshotSchema.nullable()), method(_void(), SystemMetricsSchema), method(object({ dirPath: string() }), DiskSpaceInfoSchema), method(_void(), MetricsGpuInfoSchema.nullable()), method(_void(), number().nullable()), method(object({ pids: array(number()) }), array(PidResourceStatsSchema)), method(_void(), array(AddonInstanceSchema).readonly()), method(object({ addonId: string() }), PidResourceStatsSchema.nullable()), method(_void(), array(NodeProcessSchema).readonly()), method(GetLoadSeriesInputSchema, NodeLoadSeriesSchema);
43946
- method(object({
43947
- sourceUrl: string(),
43948
- metadata: ModelConvertMetadataSchema,
43949
- targets: array(ConvertTargetSchema).min(1).readonly(),
43950
- calibrationRef: string().optional(),
43951
- sessionId: string().optional()
43952
- }), ConvertResultSchema, {
43953
- kind: "mutation",
43954
- auth: "admin",
43955
- timeoutMs: 6e5
43956
- });
43957
- method(object({
43958
- targetNodeId: string(),
43959
- modelId: string(),
43960
- format: _enum(MODEL_FORMATS),
43961
- entry: ModelCatalogEntrySchema
43962
- }), object({
43963
- ok: boolean(),
43964
- /** sha256 of the staged tarball (empty for a hub-local no-op). */
43965
- sha256: string(),
43966
- bytes: number(),
43967
- /** The target node's modelsDir the artifact landed in. */
43968
- path: string()
43969
- }), {
43970
- kind: "mutation",
43971
- auth: "admin"
43972
- });
43973
- var MotionRegionSchema = object({
43974
- bbox: BoundingBoxSchema,
43975
- pixelCount: number(),
43976
- intensity: number()
43977
- });
43978
- var MotionAnalysisResultSchema = object({
43979
- detected: boolean(),
43980
- regionCount: number(),
43981
- /** Regions passing minArea filter — used for pipeline triggering. */
43982
- regions: array(MotionRegionSchema).readonly(),
43983
- /** All raw regions from CCL before minArea filter — used for UI debug overlay. */
43984
- rawRegions: array(MotionRegionSchema).readonly(),
43985
- frameWidth: number(),
43986
- frameHeight: number(),
43987
- analysisMs: number()
43988
- });
43989
- DeviceType.Camera, method(object({
43990
- deviceId: number(),
43991
- frame: FrameInputSchema.optional(),
43992
- frameHandle: FrameHandleSchema.optional()
43993
- }), MotionAnalysisResultSchema, {
43994
- role: "admin",
43995
- kind: "mutation"
43996
- }), method(object({ deviceId: number() }), _void(), {
43997
- role: "admin",
43998
- kind: "mutation"
43999
- }), method(_void(), _void(), {
44000
- role: "admin",
44001
- kind: "mutation"
44002
- }), object({
44003
- deviceId: number(),
44004
- detected: boolean(),
44005
- regionCount: number()
44006
- });
44007
- /**
44008
- * `mqtt-broker` — broker-registry cap.
44009
- *
44010
- * NOT a pub/sub proxy. The cap exposes (a) a registry of configured
44011
- * MQTT brokers (external + optionally an embedded `aedes`-backed one)
44012
- * and (b) the connection details a consumer addon needs to spin up
44013
- * its OWN `mqtt.js` client.
44014
- *
44015
- * Why: pub/sub routing over the system event-bus loses fidelity
44016
- * (callback shape, QoS guarantees, will/retain semantics) and adds
44017
- * refcount bookkeeping that addons would rather own themselves. The
44018
- * canonical consumer needs raw `mqtt.js`
44019
- * features anyway — give it the connection config, get out of the way.
44020
- *
44021
- * Consumer flow:
44022
- * const cfg = await ctx.api.mqttBroker.getBrokerConfig({ id })
44023
- * const client = mqtt.connect(cfg.url, { username: cfg.username, … })
44024
- * client.subscribe('zigbee2mqtt/+')
44025
- *
44026
- * Collection mode: multiple brokers (e.g. one local mosquitto + one
44027
- * cloud bridge). The "embedded" entry (when present) is just another
44028
- * broker in the registry — its lifecycle is owned by the addon that
44029
- * spawned it.
44030
- */
44031
- var BrokerKindSchema = _enum(["external", "embedded"]);
44032
- /**
44033
- * Broker live-probe status.
44034
- *
44035
- * - `connected` — last probe completed a clean CONNACK
44036
- * - `disconnected` — no probe has run yet (cold cache)
44037
- * - `auth-failed` — CONNACK refused with auth error (RC 4 / 5)
44038
- * - `unreachable` — TCP connect timed out / refused
44039
- * - `tls-error` — TLS handshake failed (cert / SNI / cipher)
44040
- */
44041
- var BrokerStatusSchema$1 = _enum([
44042
- "connected",
44043
- "disconnected",
44044
- "auth-failed",
44045
- "unreachable",
44046
- "tls-error"
44047
- ]);
44048
- var BrokerInfoSchema$1 = object({
44049
- id: string(),
44050
- name: string(),
44051
- url: string(),
44052
- kind: BrokerKindSchema,
44053
- status: BrokerStatusSchema$1,
44054
- latencyMs: number().nullable(),
44055
- error: string().optional(),
44056
- /** Embedded brokers only: number of MQTT clients currently connected. */
44057
- connectedClients: number().int().nonnegative().optional(),
44058
- /** Epoch ms of the last live probe (external) or aedes snapshot (embedded). */
44059
- lastCheckedAt: number().optional()
44060
- });
44061
- /**
44062
- * Connection details — what a consumer needs to call
44063
- * `mqtt.connect(url, options)`. We split URL + credentials so the
44064
- * consumer can pass them as `mqtt.connect(url, { username, password })`
44065
- * instead of stuffing creds into the URL (which leaks them into logs).
44066
- */
44067
- var BrokerConnectionDetailsSchema = object({
44068
- url: string(),
44069
- username: string().optional(),
44070
- password: string().optional(),
44071
- /**
44072
- * Suggested prefix for `clientId`. Each consumer should suffix this
44073
- * with its own discriminator (addon id, instance id) so reconnects
44074
- * don't kick each other off (MQTT spec: clientId must be unique per
44075
- * broker).
44076
- */
44077
- clientIdPrefix: string().optional()
44078
- });
44079
- var AddBrokerInputSchema = object({
44080
- name: string().min(1),
44081
- url: string().regex(/^(mqtt|mqtts|ws|wss):\/\//, "URL must start with mqtt(s):// or ws(s)://"),
44082
- username: string().optional(),
44083
- password: string().optional(),
44084
- clientIdPrefix: string().optional()
44085
- });
44086
- var AddBrokerResultSchema = object({ id: string() });
44087
- var IdInputSchema = object({ id: string() });
44088
- var TestResultSchema$1 = discriminatedUnion("ok", [object({
44089
- ok: literal(true),
44090
- latencyMs: number()
44091
- }), object({
44092
- ok: literal(false),
44093
- error: string()
44094
- })]);
44095
- var StartEmbeddedInputSchema = object({
44096
- port: number().int().min(1).max(65535).default(1883),
44097
- /** Allow anonymous connect (no username/password). Default: false. */
44098
- allowAnonymous: boolean().default(false),
44099
- /** Optional shared username/password for clients. */
44100
- username: string().optional(),
44101
- password: string().optional()
44102
- });
44103
- var StartEmbeddedResultSchema = object({
44104
- id: string(),
44105
- url: string()
44106
- });
44107
- var StatusSchema = object({
44108
- brokerCount: number(),
44109
- embeddedRunning: boolean()
44110
- });
44111
- method(_void(), array(BrokerInfoSchema$1)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
44112
- kind: "mutation",
44113
- auth: "admin"
44114
- }), method(IdInputSchema, _void(), {
44115
- kind: "mutation",
44116
- auth: "admin"
44117
- }), method(IdInputSchema, TestResultSchema$1, {
44118
- kind: "mutation",
44119
- auth: "admin"
44120
- }), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, {
44121
- kind: "mutation",
44122
- auth: "admin"
44123
- }), method(IdInputSchema, _void(), {
44124
- kind: "mutation",
44125
- auth: "admin"
44126
- }), method(_void(), StatusSchema);
44127
- var NetworkEndpointSchema = object({
44128
- url: string(),
44129
- hostname: string(),
44130
- port: number(),
44131
- protocol: _enum(["http", "https"]),
44132
- /**
44133
- * Whether this address survives a restart of its provider. `false` = it is
44134
- * re-issued every time (a Quick tunnel's random `*.trycloudflare.com`); the
44135
- * hub never selects such an address AUTOMATICALLY for a role (D860), though
44136
- * it stays visible and an operator may still flag it. Absent = stable: every
44137
- * provider except a Quick tunnel owns a name that does not change.
44138
- */
44139
- stable: boolean().optional()
44140
- });
44141
- object({
44142
- url: string(),
44143
- /** Mirrors {@link NetworkEndpointSchema} `stable`; absent = stable. */
44144
- stable: boolean().optional(),
44145
- updated: boolean().optional()
44146
- });
44147
- var NetworkAccessStatusSchema = object({
44148
- connected: boolean(),
44149
- endpoint: NetworkEndpointSchema.nullable(),
44150
- error: string().optional()
44151
- });
44152
- /**
44153
- * Optional, richer endpoint shape returned by providers that expose
44154
- * MORE than one ingress concurrently (Tailscale Funnel with mixed
44155
- * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
44156
- * the originating provider config (mode + sourcePort) so the
44157
- * orchestrator UI can label rows distinctly. Providers that expose only
44158
- * one endpoint just omit `listEndpoints` from their provider impl.
44159
- */
44160
- var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
44161
- /**
44162
- * Stable id within the provider — typically `<mode>-<sourcePort>` so
44163
- * the orchestrator can dedupe across `listEndpoints` polls.
44164
- */
44165
- id: string(),
44166
- /** Operator-facing label (mirrors `MeshEndpoint.label`). */
44167
- label: string(),
44168
- /** Optional provider-specific mode tag, used for icon/colour in admin UI. */
44169
- mode: string().optional(),
44170
- /** Originating local port the ingress fronts (informational). */
44171
- sourcePort: number().optional()
44172
- });
44173
- method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
44174
- /**
44175
- * notification-output — canonical, capability-gated notification delivery.
44176
- *
44177
- * Apprise-derived model (see
44178
- * `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
44179
- * callers emit ONE canonical `Notification`; each provider declares a
44180
- * per-kind capability descriptor (`TargetKind`), and the pure degrade
44181
- * engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
44182
- * message to what the kind supports — callers never special-case a service.
44183
- *
44184
- * DESIGN DECISIONS (locked):
44185
- * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
44186
- * `setTargetEnabled`), each provider persisting via the `settings-store`
44187
- * cap. Rationale: the admin UI needs one uniform surface across the
44188
- * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
44189
- * alternative would fork the UI per addon and cannot host the
44190
- * discovery→adopt flow.
44191
- * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
44192
- * the generated cap-mount auto-`concatCollection`-fans them across every
44193
- * registered provider (notifiers addon + HA addon) so one catalog is
44194
- * routable. `send` / `testTarget` / CRUD route to ONE provider by the
44195
- * `addonId` the generated collection router extracts from the call input.
44196
- * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
44197
- * transformer) + UDS MsgPack both round-trip typed arrays — already used by
44198
- * `storage` / `storage-provider` / `recording` caps over the same path. No
44199
- * base64 fallback needed.
44200
- *
44201
- * TODO (deferred, closed-set change — separate decision): add
44202
- * `providerKind: 'notify'` so notification providers surface on the unified
44203
- * admin "Integrations" page.
44204
- */
44205
- /**
44206
- * Zentik-derived typed-media enum — the superset across every kind. Each
44207
- * adapter picks what it supports and the degrade engine filters the rest.
44208
- */
44209
- var AttachmentMediaTypeSchema = _enum([
44210
- "image",
44211
- "video",
44212
- "gif",
44213
- "audio",
44214
- "icon"
44215
- ]);
44216
- /**
44217
- * A single attachment. Exactly one of `url` (remote source, most adapters
44218
- * prefer this) or `bytes` (inline source; required for Pushover-style
44219
- * bytes-only kinds) MUST be present — the degrade engine expresses a
44220
- * url→bytes fetch as a `needsFetch` directive the adapter executes.
44221
- */
44222
- var AttachmentSchema = object({
44223
- mediaType: AttachmentMediaTypeSchema,
44224
- url: string().optional(),
44225
- bytes: _instanceof(Uint8Array).optional(),
44226
- mime: string().optional(),
44227
- name: string().optional()
44228
- }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
44229
- var NotificationFormatSchema = _enum([
44230
- "text",
44231
- "markdown",
44232
- "html"
44233
- ]);
44234
- /**
44235
- * The CLOSED icon vocabulary an action button may use.
44236
- *
44237
- * A closed set, not a free string, and that is the whole point: an arbitrary
44238
- * icon name is one that ntfy renders, zentik silently drops, and nobody
44239
- * notices — the same class of gap as a zone vocabulary nothing produced
44240
- * ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
44241
- * declares `actionIcons: false` and the degrade engine strips the field.
44242
- *
44243
- * Named by INTENT, never by glyph. "check" would tie the vocabulary to one
44244
- * renderer's icon set; "acknowledge" survives an adapter that draws it
44245
- * differently.
44246
- *
44247
- * ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
44248
- *
44249
- * These names are for US. **No adapter may forward one verbatim.** Each maps
44250
- * the whole set onto its own renderer's vocabulary through a
44251
- * `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
44252
- * fallback, so adding a member here fails every adapter's build until someone
44253
- * decides its glyph, which is the only place that decision can be made
44254
- * honestly.
44255
- *
44256
- * This paragraph is the bug. Zentik declared `actionIcons: true` and passed
44257
- * `disarm` straight through; iOS feeds that string to
44258
- * `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
44259
- * and every snooze and alarm button arrived BLANK. A pass-through is not a
44260
- * mapping, and "the field is documented" is not "the value renders".
44261
- *
44262
- * Adding a member is TRAIN-BOUND. The enum lives in the published
44263
- * `@camstack/server` closure and the cap seam validates against the HUB's copy,
44264
- * so an addon that emits a token the running hub does not know does not lose an
44265
- * icon — its whole `send` fails Zod validation and the notification never
44266
- * arrives. Never emit a new token from an addon before the train carrying it.
44267
- */
44268
- var NotificationActionIconSchema = _enum([
44269
- "acknowledge",
44270
- "dismiss",
44271
- "silence",
44272
- "snooze",
44273
- "view",
44274
- "play",
44275
- "open",
44276
- "close",
44277
- "lock",
44278
- "unlock",
44279
- "arm",
44280
- "arm-home",
44281
- "arm-away",
44282
- "arm-night",
44283
- "disarm",
44284
- "light",
44285
- "alert",
44286
- "camera"
44287
- ]);
44288
- /** A single tap-through action button. */
44289
- var NotificationActionSchema = object({
44290
- id: string(),
44291
- label: string(),
44292
- url: string().optional(),
44293
- /** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
44294
- icon: NotificationActionIconSchema.optional(),
44295
- /**
44296
- * Renders in a warning style where the notifier supports it.
44297
- *
44298
- * A HINT, never a gate. The callback's authority is its token and nothing
44299
- * else — see `notification-center/action-token.ts` for what that does and
44300
- * does not buy.
44301
- */
44302
- destructive: boolean().optional(),
44303
- /**
44304
- * How the tap should REACH the url.
44305
- *
44306
- * `navigate` (absent, and every button authored before this field) opens it:
44307
- * the phone leaves the notification and shows whatever the callback returns.
44308
- * That is right for a button whose answer the operator wants to read.
44309
- *
44310
- * `background` fires it as a POST and stays put. It exists for the buttons
44311
- * whose whole point is not to interrupt — "silence this for 30 minutes" is
44312
- * an answer to the notification, and being thrown into a browser tab to
44313
- * confirm it costs more attention than the notification did. A backend that
44314
- * cannot do a background call renders it as an ordinary link (the adapters
44315
- * fall back rather than dropping the button), so this is a preference, never
44316
- * a requirement.
44317
- */
44318
- mode: _enum(["navigate", "background"]).optional()
44319
- });
44320
- /** A delivery parameter value: what a notifier declared it takes. */
44321
- var DeliveryParamValueSchema = union([
44322
- string(),
44323
- number(),
44324
- boolean()
44325
- ]);
44326
- /**
44327
- * One extra delivery parameter a notifier KIND declares (D938). A rule that
44328
- * addresses a target of that kind may set it; a kind that declares nothing is
44329
- * never handed one. The admin UI derives its control from this descriptor —
44330
- * no editor knows a service by name.
44331
- *
44332
- * Optional fields are `.optional()`, never `.default()` (a Zod default does not
44333
- * run on the addon cap path).
44334
- */
44335
- var DeliveryParamSchema = object({
44336
- key: string().min(1),
44337
- label: string(),
44338
- /** i18n key of the label, resolved by the declaring addon's namespace. */
44339
- labelKey: string().optional(),
44340
- type: _enum([
44341
- "enum",
44342
- "number",
44343
- "boolean",
44344
- "string"
44345
- ]),
44346
- /** `enum` only: the closed set of accepted values. */
44347
- options: array(object({
44348
- value: string(),
44349
- label: string()
44350
- })).optional(),
44351
- /** `number` only. */
44352
- min: number().optional(),
44353
- max: number().optional(),
44354
- /** What the notifier does when the rule sets nothing; shown as the placeholder. */
44355
- default: DeliveryParamValueSchema.optional()
44356
- });
44357
- /**
44358
- * The canonical notification. `body` is the only hard field (Apprise model).
44359
- * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
44360
- * NOT a fixed severity enum — each kind declares its own `caps.levels` and
44361
- * the adapter maps this ordinal onto its native level. `level?` is an
44362
- * optional kind-native level id (`emergency`, `silent`, …) that overrides
44363
- * `priority` for that one target.
44364
- */
44365
- var NotificationSchema = object({
44366
- body: string(),
44367
- title: string().optional(),
44368
- format: NotificationFormatSchema.default("text"),
44369
- priority: number().int().min(1).max(5).default(3),
44370
- level: string().optional(),
44371
- attachments: array(AttachmentSchema).optional(),
44372
- clickUrl: string().optional(),
44373
- actions: array(NotificationActionSchema).optional(),
44374
- sound: string().optional(),
44375
- ttl: number().optional(),
44376
- tag: string().optional(),
44377
- deviceId: number().optional(),
44378
- eventId: string().optional(),
44379
- metadata: record(string(), unknown()).optional(),
44380
- /**
44381
- * Values for the parameters the target's kind DECLARES in
44382
- * {@link TargetKindSchema} `deliveryParams`. Already validated against the
44383
- * declaration by the sender; a notifier reads only the keys it declared.
44384
- */
44385
- delivery: record(string(), DeliveryParamValueSchema).optional()
44386
- });
44387
- /** One declared native severity/priority level for a kind. */
44388
- var TargetKindLevelSchema = object({
44389
- id: string(),
44390
- label: string(),
44391
- /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
44392
- ordinal: number().int().min(1).max(5).nullable(),
44393
- flags: object({
44394
- critical: boolean().optional(),
44395
- silent: boolean().optional(),
44396
- noPush: boolean().optional()
44397
- }).optional(),
44398
- /** e.g. Pushover `emergency` requires `retry` / `expire`. */
44399
- requires: array(string()).optional(),
44400
- description: string().optional()
44401
- });
44402
- /** The full capability block consulted before dispatch. */
44403
- var TargetKindCapsSchema = object({
44404
- attachments: object({
44405
- mediaTypes: array(AttachmentMediaTypeSchema),
44406
- mode: _enum([
44407
- "url",
44408
- "bytes",
44409
- "both"
44410
- ]),
44411
- max: number().int().nonnegative(),
44412
- maxBytes: number().int().positive().optional()
44413
- }),
44414
- /** Max action buttons (0 = none). */
44415
- actions: number().int().nonnegative(),
44416
- /**
44417
- * Whether this kind renders a per-action ICON.
44418
- *
44419
- * `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
44420
- * run on the addon cap path — three production failures in one day taught
44421
- * this repo that once. Absent is read as false by the degrade engine, which
44422
- * is the safe direction: an icon that is not rendered costs nothing, an icon
44423
- * assumed and dropped costs the operator's trust in the field.
44424
- */
44425
- actionIcons: boolean().optional(),
44426
- levels: array(TargetKindLevelSchema),
44427
- format: array(NotificationFormatSchema),
44428
- clickUrl: boolean(),
44429
- sound: boolean(),
44430
- ttl: boolean(),
44431
- bodyMaxLen: number().int().positive()
44432
- });
44433
- /**
44434
- * `configSchema` is a `ConfigUISchema` tree passed through to the admin
44435
- * FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
44436
- * `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
44437
- * the union is large and not meant for runtime validation here; the exported
44438
- * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
44439
- */
44440
- var ConfigSchemaPassthrough$2 = unknown();
44441
- var TargetKindSchema = object({
44442
- kind: string(),
44443
- label: string(),
44444
- icon: string(),
44445
- /** Stamped by each provider so the concat-fanned catalog stays routable. */
44446
- addonId: string(),
44447
- /**
44448
- * URL of the kind's bundled BRAND icon, served by the providing addon over
44449
- * its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
44450
- * when the addon bundles no icon for that kind — the client then falls back
44451
- * to a neutral glyph rather than rendering the raw `icon` NAME as text.
44452
- *
44453
- * Root-relative on purpose: it resolves against whatever origin serves a web
44454
- * client, and a native client joins it onto its own hub base.
44455
- *
44456
- * DECLARED here deliberately. It used to travel as an undeclared passthrough
44457
- * field that survived only because the runtime cap-router forwards provider
44458
- * output verbatim — so every consumer had to re-declare it by hand to stop
44459
- * its own Zod parse from stripping it, and the whole arrangement would have
44460
- * broken silently the moment output validation was tightened anywhere.
44461
- */
44462
- iconUrl: string().optional(),
44463
- /**
44464
- * Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
44465
- *
44466
- * The server knows this and therefore says it, because the client cannot
44467
- * safely guess: a React-Native client renders SVG and raster through two
44468
- * DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
44469
- * not decode SVG on iOS/Android), so without this it silently fell back to a
44470
- * placeholder glyph for every vector icon while the web build looked fine.
44471
- *
44472
- * Absent when {@link iconUrl} is absent, or for a legacy provider that has
44473
- * not been updated — a client that cannot determine the type should prefer
44474
- * its raster path, which is the safe default for an unknown image.
44481
+ * Reported because "the last hour" and "the last six hours" are different
44482
+ * questions and an operator must not have to guess which was answered.
44475
44483
  */
44476
- iconMediaType: string().optional(),
44477
- configSchema: ConfigSchemaPassthrough$2,
44484
+ durable: boolean()
44485
+ });
44486
+ var GetLoadSeriesInputSchema = object({
44478
44487
  /**
44479
- * The config keys that decide WHERE this kind's secrets are sent (server,
44480
- * base URL, instance, chat, recipient). A save that changes any of them
44481
- * while leaving a secret redacted is refused by the provider (D634), so an
44482
- * editor uses this to ask for the secret on exactly those edits instead of
44483
- * on every edit.
44488
+ * The node whose series is wanted.
44484
44489
  *
44485
- * Optional: absent from a provider that holds no secret (the Home Assistant
44486
- * kinds) and from a provider older than this field — an editor that finds
44487
- * it absent must assume ANY edit may need the secret.
44490
+ * NOT named `nodeId`: the generated cap router strips a top-level
44491
+ * `nodeId` from every method input and uses it to ROUTE the call to
44492
+ * that node's provider (`generated-cap-routers.ts`). A series target
44493
+ * called `nodeId` would silently become a routing pin and never reach
44494
+ * the provider. The hub holds every node it hears from, so the
44495
+ * ordinary call is unpinned — answered by the hub, for any node.
44488
44496
  */
44489
- destinationKeys: array(string()).optional(),
44490
- supportsDiscovery: boolean(),
44497
+ forNodeId: string(),
44491
44498
  /**
44492
- * Extra delivery parameters a rule may set for a target of this kind (D938).
44493
- * Absent = none; the rule editor then offers nothing.
44499
+ * EXCLUSIVE lower bound. A caller passes the newest `atMs` it already
44500
+ * holds and receives only what it is missing, so seeding a live chart
44501
+ * from this method cannot double a point already drawn.
44494
44502
  */
44495
- deliveryParams: array(DeliveryParamSchema).optional(),
44496
- caps: TargetKindCapsSchema
44503
+ sinceMs: number().optional(),
44504
+ /**
44505
+ * Most points the caller wants PER FUNCTION. The window is reduced to fit,
44506
+ * preserving min and max per bucket.
44507
+ *
44508
+ * Absent means NO reduction — legitimate for a short window and a trap for a
44509
+ * long one, which is why a chart passes its own pixel width.
44510
+ */
44511
+ maxPoints: number().int().positive().optional()
44512
+ });
44513
+ var SystemMetricsSchema = object({
44514
+ cpuPercent: number(),
44515
+ /** `null` when the memory sample failed or was not taken yet (D393). */
44516
+ memoryPercent: number().nullable(),
44517
+ memoryUsedMB: number().nullable(),
44518
+ memoryTotalMB: number(),
44519
+ diskPercent: number().optional(),
44520
+ temperature: number().optional(),
44521
+ gpuPercent: number().optional(),
44522
+ gpuMemoryPercent: number().optional()
44523
+ });
44524
+ method(_void(), SystemResourceSnapshotSchema), method(_void(), SystemResourceSnapshotSchema.nullable()), method(_void(), SystemMetricsSchema), method(object({ dirPath: string() }), DiskSpaceInfoSchema), method(_void(), MetricsGpuInfoSchema.nullable()), method(_void(), number().nullable()), method(object({ pids: array(number()) }), array(PidResourceStatsSchema)), method(_void(), array(AddonInstanceSchema).readonly()), method(object({ addonId: string() }), PidResourceStatsSchema.nullable()), method(_void(), array(NodeProcessSchema).readonly()), method(GetLoadSeriesInputSchema, NodeLoadSeriesSchema);
44525
+ method(object({
44526
+ sourceUrl: string(),
44527
+ metadata: ModelConvertMetadataSchema,
44528
+ targets: array(ConvertTargetSchema).min(1).readonly(),
44529
+ calibrationRef: string().optional(),
44530
+ sessionId: string().optional()
44531
+ }), ConvertResultSchema, {
44532
+ kind: "mutation",
44533
+ auth: "admin",
44534
+ timeoutMs: 6e5
44535
+ });
44536
+ method(object({
44537
+ targetNodeId: string(),
44538
+ modelId: string(),
44539
+ format: _enum(MODEL_FORMATS),
44540
+ entry: ModelCatalogEntrySchema
44541
+ }), object({
44542
+ ok: boolean(),
44543
+ /** sha256 of the staged tarball (empty for a hub-local no-op). */
44544
+ sha256: string(),
44545
+ bytes: number(),
44546
+ /** The target node's modelsDir the artifact landed in. */
44547
+ path: string()
44548
+ }), {
44549
+ kind: "mutation",
44550
+ auth: "admin"
44551
+ });
44552
+ var MotionRegionSchema = object({
44553
+ bbox: BoundingBoxSchema,
44554
+ pixelCount: number(),
44555
+ intensity: number()
44556
+ });
44557
+ var MotionAnalysisResultSchema = object({
44558
+ detected: boolean(),
44559
+ regionCount: number(),
44560
+ /** Regions passing minArea filter — used for pipeline triggering. */
44561
+ regions: array(MotionRegionSchema).readonly(),
44562
+ /** All raw regions from CCL before minArea filter — used for UI debug overlay. */
44563
+ rawRegions: array(MotionRegionSchema).readonly(),
44564
+ frameWidth: number(),
44565
+ frameHeight: number(),
44566
+ analysisMs: number()
44567
+ });
44568
+ DeviceType.Camera, method(object({
44569
+ deviceId: number(),
44570
+ frame: FrameInputSchema.optional(),
44571
+ frameHandle: FrameHandleSchema.optional()
44572
+ }), MotionAnalysisResultSchema, {
44573
+ role: "admin",
44574
+ kind: "mutation"
44575
+ }), method(object({ deviceId: number() }), _void(), {
44576
+ role: "admin",
44577
+ kind: "mutation"
44578
+ }), method(_void(), _void(), {
44579
+ role: "admin",
44580
+ kind: "mutation"
44581
+ }), object({
44582
+ deviceId: number(),
44583
+ detected: boolean(),
44584
+ regionCount: number()
44497
44585
  });
44498
44586
  /**
44499
- * A persisted target. `config` holds secrets; providers REDACT secret fields
44500
- * (return a presence marker only) when serving `listTargets` — never
44501
- * round-trip a stored secret to the UI.
44587
+ * `mqtt-broker` — broker-registry cap.
44588
+ *
44589
+ * NOT a pub/sub proxy. The cap exposes (a) a registry of configured
44590
+ * MQTT brokers (external + optionally an embedded `aedes`-backed one)
44591
+ * and (b) the connection details a consumer addon needs to spin up
44592
+ * its OWN `mqtt.js` client.
44593
+ *
44594
+ * Why: pub/sub routing over the system event-bus loses fidelity
44595
+ * (callback shape, QoS guarantees, will/retain semantics) and adds
44596
+ * refcount bookkeeping that addons would rather own themselves. The
44597
+ * canonical consumer needs raw `mqtt.js`
44598
+ * features anyway — give it the connection config, get out of the way.
44599
+ *
44600
+ * Consumer flow:
44601
+ * const cfg = await ctx.api.mqttBroker.getBrokerConfig({ id })
44602
+ * const client = mqtt.connect(cfg.url, { username: cfg.username, … })
44603
+ * client.subscribe('zigbee2mqtt/+')
44604
+ *
44605
+ * Collection mode: multiple brokers (e.g. one local mosquitto + one
44606
+ * cloud bridge). The "embedded" entry (when present) is just another
44607
+ * broker in the registry — its lifecycle is owned by the addon that
44608
+ * spawned it.
44502
44609
  */
44503
- var TargetSchema = object({
44610
+ var BrokerKindSchema = _enum(["external", "embedded"]);
44611
+ /**
44612
+ * Broker live-probe status.
44613
+ *
44614
+ * - `connected` — last probe completed a clean CONNACK
44615
+ * - `disconnected` — no probe has run yet (cold cache)
44616
+ * - `auth-failed` — CONNACK refused with auth error (RC 4 / 5)
44617
+ * - `unreachable` — TCP connect timed out / refused
44618
+ * - `tls-error` — TLS handshake failed (cert / SNI / cipher)
44619
+ */
44620
+ var BrokerStatusSchema$1 = _enum([
44621
+ "connected",
44622
+ "disconnected",
44623
+ "auth-failed",
44624
+ "unreachable",
44625
+ "tls-error"
44626
+ ]);
44627
+ var BrokerInfoSchema$1 = object({
44504
44628
  id: string(),
44505
44629
  name: string(),
44506
- kind: string(),
44507
- addonId: string(),
44508
- enabled: boolean(),
44509
- config: record(string(), unknown())
44510
- });
44511
- /** A discovery-surfaced candidate (config is partial + non-secret). */
44512
- var DiscoveredTargetSchema = object({
44513
- kind: string(),
44514
- suggestedName: string(),
44515
- config: record(string(), unknown())
44516
- });
44517
- /** The degrade engine's report — what was resolved / dropped / degraded. */
44518
- var RenderedAsSchema = object({
44519
- level: string(),
44520
- format: NotificationFormatSchema,
44521
- attachmentsSent: number().int().nonnegative(),
44522
- actionsSent: number().int().nonnegative(),
44523
- truncated: boolean(),
44524
- dropped: array(string())
44525
- });
44526
- var SendResultSchema = object({
44527
- success: boolean(),
44630
+ url: string(),
44631
+ kind: BrokerKindSchema,
44632
+ status: BrokerStatusSchema$1,
44633
+ latencyMs: number().nullable(),
44528
44634
  error: string().optional(),
44529
- renderedAs: RenderedAsSchema.optional()
44635
+ /** Embedded brokers only: number of MQTT clients currently connected. */
44636
+ connectedClients: number().int().nonnegative().optional(),
44637
+ /** Epoch ms of the last live probe (external) or aedes snapshot (embedded). */
44638
+ lastCheckedAt: number().optional()
44530
44639
  });
44531
- /** Same shape as SendResult — kept as a distinct name for the test panel. */
44532
- var TestResultSchema = SendResultSchema;
44533
44640
  /**
44534
- * Pre-save credential probe against a partial (unsaved) target config.
44535
- * `resolvedUrl` never includes secrets (token stripped from path).
44641
+ * Connection details — what a consumer needs to call
44642
+ * `mqtt.connect(url, options)`. We split URL + credentials so the
44643
+ * consumer can pass them as `mqtt.connect(url, { username, password })`
44644
+ * instead of stuffing creds into the URL (which leaks them into logs).
44536
44645
  */
44537
- var ProbeTargetConfigResultSchema = object({
44538
- ok: boolean(),
44539
- resolvedUrl: string(),
44540
- httpStatus: number().int().optional(),
44541
- stateInstance: string().optional(),
44542
- error: string().optional()
44543
- });
44544
- method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema), { auth: "admin" }), method(object({ targetId: string() }), TargetSchema, { auth: "admin" }), method(object({
44545
- kind: string(),
44546
- config: record(string(), unknown()).optional(),
44646
+ var BrokerConnectionDetailsSchema = object({
44647
+ url: string(),
44648
+ username: string().optional(),
44649
+ password: string().optional(),
44547
44650
  /**
44548
- * When editing / duplicating, the blank password field never round-trips
44549
- * the stored token. Pass the source target id so the provider can restore
44550
- * secrets server-side before dialling (same merge as upsert).
44651
+ * Suggested prefix for `clientId`. Each consumer should suffix this
44652
+ * with its own discriminator (addon id, instance id) so reconnects
44653
+ * don't kick each other off (MQTT spec: clientId must be unique per
44654
+ * broker).
44551
44655
  */
44552
- targetId: string().optional()
44553
- }), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
44554
- kind: string(),
44555
- config: record(string(), unknown()),
44556
- /** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
44557
- targetId: string().optional()
44558
- }), ProbeTargetConfigResultSchema, {
44559
- kind: "mutation",
44560
- auth: "admin"
44561
- }), method(object({
44562
- targetId: string(),
44563
- notification: NotificationSchema
44564
- }), SendResultSchema, {
44656
+ clientIdPrefix: string().optional()
44657
+ });
44658
+ var AddBrokerInputSchema = object({
44659
+ name: string().min(1),
44660
+ url: string().regex(/^(mqtt|mqtts|ws|wss):\/\//, "URL must start with mqtt(s):// or ws(s)://"),
44661
+ username: string().optional(),
44662
+ password: string().optional(),
44663
+ clientIdPrefix: string().optional()
44664
+ });
44665
+ var AddBrokerResultSchema = object({ id: string() });
44666
+ var IdInputSchema = object({ id: string() });
44667
+ var TestResultSchema$1 = discriminatedUnion("ok", [object({
44668
+ ok: literal(true),
44669
+ latencyMs: number()
44670
+ }), object({
44671
+ ok: literal(false),
44672
+ error: string()
44673
+ })]);
44674
+ var StartEmbeddedInputSchema = object({
44675
+ port: number().int().min(1).max(65535).default(1883),
44676
+ /** Allow anonymous connect (no username/password). Default: false. */
44677
+ allowAnonymous: boolean().default(false),
44678
+ /** Optional shared username/password for clients. */
44679
+ username: string().optional(),
44680
+ password: string().optional()
44681
+ });
44682
+ var StartEmbeddedResultSchema = object({
44683
+ id: string(),
44684
+ url: string()
44685
+ });
44686
+ var StatusSchema = object({
44687
+ brokerCount: number(),
44688
+ embeddedRunning: boolean()
44689
+ });
44690
+ method(_void(), array(BrokerInfoSchema$1)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
44565
44691
  kind: "mutation",
44566
44692
  auth: "admin"
44567
- }), method(object({
44568
- targetId: string(),
44569
- sample: NotificationSchema.optional()
44570
- }), TestResultSchema, {
44693
+ }), method(IdInputSchema, _void(), {
44571
44694
  kind: "mutation",
44572
44695
  auth: "admin"
44573
- }), method(object({
44574
- target: TargetSchema,
44575
- /**
44576
- * Duplicate flow: create a NEW target id while copying secrets from an
44577
- * existing row. The UI never holds the stored token; blank / redacted
44578
- * password fields are restored from this source (D634 still applies if
44579
- * destinations diverge without a re-typed secret).
44580
- */
44581
- cloneSecretsFrom: string().optional()
44582
- }), TargetSchema, {
44696
+ }), method(IdInputSchema, TestResultSchema$1, {
44583
44697
  kind: "mutation",
44584
44698
  auth: "admin"
44585
- }), method(object({ targetId: string() }), _void(), {
44699
+ }), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, {
44586
44700
  kind: "mutation",
44587
44701
  auth: "admin"
44588
- }), method(object({
44589
- targetId: string(),
44590
- enabled: boolean()
44591
- }), _void(), {
44702
+ }), method(IdInputSchema, _void(), {
44592
44703
  kind: "mutation",
44593
44704
  auth: "admin"
44705
+ }), method(_void(), StatusSchema);
44706
+ var NetworkEndpointSchema = object({
44707
+ url: string(),
44708
+ hostname: string(),
44709
+ port: number(),
44710
+ protocol: _enum(["http", "https"]),
44711
+ /**
44712
+ * Whether this address survives a restart of its provider. `false` = it is
44713
+ * re-issued every time (a Quick tunnel's random `*.trycloudflare.com`); the
44714
+ * hub never selects such an address AUTOMATICALLY for a role (D860), though
44715
+ * it stays visible and an operator may still flag it. Absent = stable: every
44716
+ * provider except a Quick tunnel owns a name that does not change.
44717
+ */
44718
+ stable: boolean().optional()
44719
+ });
44720
+ object({
44721
+ url: string(),
44722
+ /** Mirrors {@link NetworkEndpointSchema} `stable`; absent = stable. */
44723
+ stable: boolean().optional(),
44724
+ updated: boolean().optional()
44725
+ });
44726
+ var NetworkAccessStatusSchema = object({
44727
+ connected: boolean(),
44728
+ endpoint: NetworkEndpointSchema.nullable(),
44729
+ error: string().optional()
44730
+ });
44731
+ /**
44732
+ * Optional, richer endpoint shape returned by providers that expose
44733
+ * MORE than one ingress concurrently (Tailscale Funnel with mixed
44734
+ * serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
44735
+ * the originating provider config (mode + sourcePort) so the
44736
+ * orchestrator UI can label rows distinctly. Providers that expose only
44737
+ * one endpoint just omit `listEndpoints` from their provider impl.
44738
+ */
44739
+ var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
44740
+ /**
44741
+ * Stable id within the provider — typically `<mode>-<sourcePort>` so
44742
+ * the orchestrator can dedupe across `listEndpoints` polls.
44743
+ */
44744
+ id: string(),
44745
+ /** Operator-facing label (mirrors `MeshEndpoint.label`). */
44746
+ label: string(),
44747
+ /** Optional provider-specific mode tag, used for icon/colour in admin UI. */
44748
+ mode: string().optional(),
44749
+ /** Originating local port the ingress fronts (informational). */
44750
+ sourcePort: number().optional()
44594
44751
  });
44752
+ method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
44595
44753
  var AUDIO_MACRO_LABELS = [
44596
44754
  {
44597
44755
  id: "speech",
@@ -50992,14 +51150,6 @@ var EngineProvisioningSchema = object({
50992
51150
  */
50993
51151
  configIssues: array(string()).optional()
50994
51152
  });
50995
- var PipelineStepInputSchema = lazy(() => object({
50996
- addonId: string(),
50997
- modelId: string().optional(),
50998
- enabled: boolean().default(true),
50999
- children: array(PipelineStepInputSchema).optional(),
51000
- settings: record(string(), unknown()).optional(),
51001
- jumpDeviceKey: string().optional()
51002
- }));
51003
51153
  var ModelSubstitutionSchema = object({
51004
51154
  addonId: string(),
51005
51155
  chosen: string(),
@@ -51078,7 +51228,6 @@ var ConfigUISchemaBridge = custom();
51078
51228
  var ConfigUISchemaNullableBridge = custom();
51079
51229
  var InferenceCapabilitiesBridge = custom();
51080
51230
  var ModelAvailabilityListBridge = custom();
51081
- var PipelineRunResultBridge = custom();
51082
51231
  method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
51083
51232
  modelId: string(),
51084
51233
  settings: record(string(), unknown()).readonly()
@@ -58752,7 +58901,7 @@ object({
58752
58901
  /** Ms epoch of the last value received from the appliance; 0 = never. */
58753
58902
  lastFetchedAt: number()
58754
58903
  });
58755
- var DeviceIdSchema = number().int().nonnegative();
58904
+ var DeviceIdSchema$1 = number().int().nonnegative();
58756
58905
  /** A value a program option or setting can take. */
58757
58906
  var DishwasherValueSchema = union([
58758
58907
  boolean(),
@@ -58816,77 +58965,77 @@ var DishwasherOptionsSchema = object({
58816
58965
  options: array(DishwasherOptionDescriptorSchema)
58817
58966
  });
58818
58967
  DeviceType.Dishwasher, method(object({
58819
- deviceId: DeviceIdSchema,
58968
+ deviceId: DeviceIdSchema$1,
58820
58969
  program: string().min(1).optional(),
58821
58970
  options: record(string(), DishwasherValueSchema).optional()
58822
58971
  }), _void(), {
58823
58972
  kind: "mutation",
58824
58973
  auth: "protected"
58825
- }), method(object({ deviceId: DeviceIdSchema }), _void(), {
58974
+ }), method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
58826
58975
  kind: "mutation",
58827
58976
  auth: "protected"
58828
- }), method(object({ deviceId: DeviceIdSchema }), _void(), {
58977
+ }), method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
58829
58978
  kind: "mutation",
58830
58979
  auth: "protected"
58831
- }), method(object({ deviceId: DeviceIdSchema }), _void(), {
58980
+ }), method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
58832
58981
  kind: "mutation",
58833
58982
  auth: "protected"
58834
58983
  }), method(object({
58835
- deviceId: DeviceIdSchema,
58984
+ deviceId: DeviceIdSchema$1,
58836
58985
  program: string().min(1)
58837
58986
  }), _void(), {
58838
58987
  kind: "mutation",
58839
58988
  auth: "protected"
58840
58989
  }), method(object({
58841
- deviceId: DeviceIdSchema,
58990
+ deviceId: DeviceIdSchema$1,
58842
58991
  on: boolean()
58843
58992
  }), _void(), {
58844
58993
  kind: "mutation",
58845
58994
  auth: "protected"
58846
58995
  }), method(object({
58847
- deviceId: DeviceIdSchema,
58996
+ deviceId: DeviceIdSchema$1,
58848
58997
  on: boolean()
58849
58998
  }), _void(), {
58850
58999
  kind: "mutation",
58851
59000
  auth: "protected"
58852
59001
  }), method(object({
58853
- deviceId: DeviceIdSchema,
59002
+ deviceId: DeviceIdSchema$1,
58854
59003
  on: boolean()
58855
59004
  }), _void(), {
58856
59005
  kind: "mutation",
58857
59006
  auth: "protected"
58858
59007
  }), method(object({
58859
- deviceId: DeviceIdSchema,
59008
+ deviceId: DeviceIdSchema$1,
58860
59009
  on: boolean()
58861
59010
  }), _void(), {
58862
59011
  kind: "mutation",
58863
59012
  auth: "protected"
58864
59013
  }), method(object({
58865
- deviceId: DeviceIdSchema,
59014
+ deviceId: DeviceIdSchema$1,
58866
59015
  on: boolean()
58867
59016
  }), _void(), {
58868
59017
  kind: "mutation",
58869
59018
  auth: "protected"
58870
59019
  }), method(object({
58871
- deviceId: DeviceIdSchema,
59020
+ deviceId: DeviceIdSchema$1,
58872
59021
  on: boolean()
58873
59022
  }), _void(), {
58874
59023
  kind: "mutation",
58875
59024
  auth: "protected"
58876
59025
  }), method(object({
58877
- deviceId: DeviceIdSchema,
59026
+ deviceId: DeviceIdSchema$1,
58878
59027
  on: boolean()
58879
59028
  }), _void(), {
58880
59029
  kind: "mutation",
58881
59030
  auth: "protected"
58882
59031
  }), method(object({
58883
- deviceId: DeviceIdSchema,
59032
+ deviceId: DeviceIdSchema$1,
58884
59033
  key: string().min(1),
58885
59034
  value: DishwasherValueSchema
58886
59035
  }), _void(), {
58887
59036
  kind: "mutation",
58888
59037
  auth: "protected"
58889
- }), method(object({ deviceId: DeviceIdSchema }), DishwasherOptionsSchema, { auth: "protected" });
59038
+ }), method(object({ deviceId: DeviceIdSchema$1 }), DishwasherOptionsSchema, { auth: "protected" });
58890
59039
  DeviceType.Camera;
58891
59040
  object({
58892
59041
  /** Ms epoch of the last press. null = never observed since this provider started. */
@@ -61324,7 +61473,8 @@ var TopologyNodeSchema = object({
61324
61473
  isHub: boolean(),
61325
61474
  isOnline: boolean(),
61326
61475
  cpuPercent: number(),
61327
- memoryPercent: number(),
61476
+ /** `null` when the node's memory read failed or it has not reported one (D393). */
61477
+ memoryPercent: number().nullable(),
61328
61478
  uptime: number(),
61329
61479
  lastSeen: string(),
61330
61480
  localIps: array(string()).readonly(),
@@ -61859,9 +62009,10 @@ object({
61859
62009
  * combined reading; per-hopper levels live in `food1`/`food2`. Null when
61860
62010
  * the device has not reported yet. */
61861
62011
  foodLevel: number().nullable().meta(readingMeta("none")),
61862
- /** Food in the BOWL as the feeder reports it (`state.bowl`), in the
61863
- * feeder's own units. Null when the model reports none. */
61864
- bowlFood: number().nullable().meta(readingMeta("none")),
62012
+ /** Food in the BOWL, a mass (stored in kilograms, D944). Reported only by a
62013
+ * feeder with a bowl scale; null when the model has none or its reading is
62014
+ * of an unproven unit. */
62015
+ bowlFood: number().nullable().meta(readingMeta("mass")),
61865
62016
  /** True while the pet is eating (`state.eating`). */
61866
62017
  eating: boolean(),
61867
62018
  /** Whether `markFoodReplenished` does anything on this model. When false
@@ -61974,6 +62125,64 @@ DeviceType.PetFeeder, method(object({
61974
62125
  kind: "mutation",
61975
62126
  auth: "protected"
61976
62127
  });
62128
+ object({
62129
+ /** Water in the tank, 0..100 (%). Null while the source has not reported. */
62130
+ waterLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
62131
+ /** Water in the tank, in litres. Null while the source has not reported. */
62132
+ waterVolume: number().min(0).nullable().meta(readingMeta("volume")),
62133
+ /** True when the tank is empty. Null while unknown. */
62134
+ empty: boolean().nullable(),
62135
+ /** True while the pump is running. Null while unknown. */
62136
+ pumpRunning: boolean().nullable(),
62137
+ /** True when the pump works on an on/off duty cycle rather than continuously. */
62138
+ pumpIntervalEnabled: boolean().nullable(),
62139
+ /** How long the pump runs per cycle, in seconds. */
62140
+ pumpOnTime: number().min(0).nullable().meta(readingMeta("duration")),
62141
+ /** How long the pump rests per cycle, in seconds. */
62142
+ pumpOffTime: number().min(0).nullable().meta(readingMeta("duration")),
62143
+ /** Water drunk in the last drink, in litres. */
62144
+ lastDrink: number().min(0).nullable().meta(readingMeta("volume")),
62145
+ /** Water drunk in the current hour, in litres. */
62146
+ currentHourDrink: number().min(0).nullable().meta(readingMeta("volume")),
62147
+ /** Water drunk over the last 24 hours (rolling), in litres. */
62148
+ last24hDrink: number().min(0).nullable().meta(readingMeta("volume")),
62149
+ /** Water drunk since local midnight, in litres. */
62150
+ todayDrink: number().min(0).nullable().meta(readingMeta("volume")),
62151
+ /** Decoded fault message. Null means healthy; a non-null string is an active fault. */
62152
+ error: string().nullable(),
62153
+ /** Ms epoch when the slice was last refreshed. */
62154
+ lastFetchedAt: number()
62155
+ });
62156
+ var DeviceIdSchema = number().int().nonnegative();
62157
+ var MinutesSchema = number().int().min(0);
62158
+ DeviceType.PetFountain, method(object({
62159
+ deviceId: DeviceIdSchema,
62160
+ on: boolean()
62161
+ }), _void(), {
62162
+ kind: "mutation",
62163
+ auth: "protected"
62164
+ }), method(object({
62165
+ deviceId: DeviceIdSchema,
62166
+ on: boolean()
62167
+ }), _void(), {
62168
+ kind: "mutation",
62169
+ auth: "protected"
62170
+ }), method(object({
62171
+ deviceId: DeviceIdSchema,
62172
+ minutes: MinutesSchema
62173
+ }), _void(), {
62174
+ kind: "mutation",
62175
+ auth: "protected"
62176
+ }), method(object({
62177
+ deviceId: DeviceIdSchema,
62178
+ minutes: MinutesSchema
62179
+ }), _void(), {
62180
+ kind: "mutation",
62181
+ auth: "protected"
62182
+ }), method(object({ deviceId: DeviceIdSchema }), _void(), {
62183
+ kind: "mutation",
62184
+ auth: "protected"
62185
+ });
61977
62186
  var VehicleSchema = object({
61978
62187
  id: string(),
61979
62188
  name: string(),
@@ -66571,7 +66780,7 @@ discriminatedUnion("kind", [object({
66571
66780
  })]);
66572
66781
  DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
66573
66782
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
66574
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
66783
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
66575
66784
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
66576
66785
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
66577
66786
  new Set(CAP_NAMES);
@@ -66624,7 +66833,7 @@ var AccessoryKind = {
66624
66833
  PrivacyMask: DeviceRole.PrivacyMask
66625
66834
  };
66626
66835
  AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
66627
- DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
66836
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
66628
66837
  new Set(Object.values(DeviceType));
66629
66838
  Object.freeze({
66630
66839
  "accessories.getStatus": {
@@ -68559,6 +68768,13 @@ Object.freeze({
68559
68768
  access: "create",
68560
68769
  role: "admin"
68561
68770
  },
68771
+ "deviceExport.exposeEntity": {
68772
+ capName: "device-export",
68773
+ capScope: "system",
68774
+ addonId: null,
68775
+ access: "create",
68776
+ role: "admin"
68777
+ },
68562
68778
  "deviceExport.getDeviceLiveContribution": {
68563
68779
  capName: "device-export",
68564
68780
  capScope: "system",
@@ -68587,6 +68803,13 @@ Object.freeze({
68587
68803
  access: "view",
68588
68804
  role: "admin"
68589
68805
  },
68806
+ "deviceExport.listExposedEntities": {
68807
+ capName: "device-export",
68808
+ capScope: "system",
68809
+ addonId: null,
68810
+ access: "view",
68811
+ role: "admin"
68812
+ },
68590
68813
  "deviceExport.listSupportedDeviceKinds": {
68591
68814
  capName: "device-export",
68592
68815
  capScope: "system",
@@ -68594,6 +68817,13 @@ Object.freeze({
68594
68817
  access: "view",
68595
68818
  role: "admin"
68596
68819
  },
68820
+ "deviceExport.listSupportedEntityKinds": {
68821
+ capName: "device-export",
68822
+ capScope: "system",
68823
+ addonId: null,
68824
+ access: "view",
68825
+ role: "admin"
68826
+ },
68597
68827
  "deviceExport.unexposeDevice": {
68598
68828
  capName: "device-export",
68599
68829
  capScope: "system",
@@ -68601,6 +68831,13 @@ Object.freeze({
68601
68831
  access: "create",
68602
68832
  role: "admin"
68603
68833
  },
68834
+ "deviceExport.unexposeEntity": {
68835
+ capName: "device-export",
68836
+ capScope: "system",
68837
+ addonId: null,
68838
+ access: "create",
68839
+ role: "admin"
68840
+ },
68604
68841
  "deviceExtension.listCandidates": {
68605
68842
  capName: "device-extension",
68606
68843
  capScope: "device",
@@ -71422,6 +71659,13 @@ Object.freeze({
71422
71659
  access: "create",
71423
71660
  role: "admin"
71424
71661
  },
71662
+ "nodeRemovalHook.onNodeRemoved": {
71663
+ capName: "node-removal-hook",
71664
+ capScope: "system",
71665
+ addonId: null,
71666
+ access: "create",
71667
+ role: "admin"
71668
+ },
71425
71669
  "nodes.clusterAddonStatus": {
71426
71670
  capName: "nodes",
71427
71671
  capScope: "system",
@@ -72059,6 +72303,48 @@ Object.freeze({
72059
72303
  access: "create",
72060
72304
  role: "viewer"
72061
72305
  },
72306
+ "petFountain.getStatus": {
72307
+ capName: "pet-fountain",
72308
+ capScope: "device",
72309
+ addonId: null,
72310
+ access: "view",
72311
+ role: "viewer"
72312
+ },
72313
+ "petFountain.resetDailyConsumption": {
72314
+ capName: "pet-fountain",
72315
+ capScope: "device",
72316
+ addonId: null,
72317
+ access: "delete",
72318
+ role: "viewer"
72319
+ },
72320
+ "petFountain.setPump": {
72321
+ capName: "pet-fountain",
72322
+ capScope: "device",
72323
+ addonId: null,
72324
+ access: "create",
72325
+ role: "viewer"
72326
+ },
72327
+ "petFountain.setPumpInterval": {
72328
+ capName: "pet-fountain",
72329
+ capScope: "device",
72330
+ addonId: null,
72331
+ access: "create",
72332
+ role: "viewer"
72333
+ },
72334
+ "petFountain.setPumpOffMinutes": {
72335
+ capName: "pet-fountain",
72336
+ capScope: "device",
72337
+ addonId: null,
72338
+ access: "create",
72339
+ role: "viewer"
72340
+ },
72341
+ "petFountain.setPumpOnMinutes": {
72342
+ capName: "pet-fountain",
72343
+ capScope: "device",
72344
+ addonId: null,
72345
+ access: "create",
72346
+ role: "viewer"
72347
+ },
72062
72348
  "pipelineAnalytics.addRetrainFrameFromRecording": {
72063
72349
  capName: "pipeline-analytics",
72064
72350
  capScope: "device",
@@ -78541,6 +78827,100 @@ var APPLIANCE_CAP_VOCABULARY = {
78541
78827
  }
78542
78828
  ]
78543
78829
  },
78830
+ "pet-fountain": {
78831
+ scope: "device",
78832
+ noun: t("Pet fountain", "Fontana per animali"),
78833
+ actions: [
78834
+ {
78835
+ id: "petFountain.pumpOn",
78836
+ method: "setPump",
78837
+ label: t("Pump on", "Pompa accesa"),
78838
+ phrase: t("Start the pump of {device}", "Avvia la pompa di {device}"),
78839
+ icon: "play",
78840
+ args: { on: true }
78841
+ },
78842
+ {
78843
+ id: "petFountain.pumpOff",
78844
+ method: "setPump",
78845
+ label: t("Pump off", "Pompa spenta"),
78846
+ phrase: t("Stop the pump of {device}", "Ferma la pompa di {device}"),
78847
+ icon: "square",
78848
+ args: { on: false }
78849
+ },
78850
+ {
78851
+ id: "petFountain.intervalOn",
78852
+ method: "setPumpInterval",
78853
+ label: t("Pump cycle on", "Ciclo pompa attivo"),
78854
+ phrase: t("Run the pump of {device} on its on/off cycle", "Fai lavorare la pompa di {device} a ciclo acceso/spento"),
78855
+ icon: "timer",
78856
+ args: { on: true }
78857
+ },
78858
+ {
78859
+ id: "petFountain.intervalOff",
78860
+ method: "setPumpInterval",
78861
+ label: t("Pump cycle off", "Ciclo pompa spento"),
78862
+ phrase: t("Run the pump of {device} continuously", "Fai lavorare la pompa di {device} in continuo"),
78863
+ icon: "timer-off",
78864
+ args: { on: false }
78865
+ },
78866
+ {
78867
+ id: "petFountain.pumpOnMinutes",
78868
+ method: "setPumpOnMinutes",
78869
+ label: t("Set pump run time", "Imposta il tempo di lavoro della pompa"),
78870
+ phrase: t("Set the pump of {device} to run {minutes} minutes per cycle", "Imposta la pompa di {device} a lavorare {minutes} minuti per ciclo"),
78871
+ icon: "timer",
78872
+ openArgs: [{
78873
+ key: "minutes",
78874
+ label: t("Minutes", "Minuti"),
78875
+ offer: {
78876
+ kind: "number",
78877
+ min: 0,
78878
+ step: 1,
78879
+ unit: "min"
78880
+ }
78881
+ }]
78882
+ },
78883
+ {
78884
+ id: "petFountain.pumpOffMinutes",
78885
+ method: "setPumpOffMinutes",
78886
+ label: t("Set pump rest time", "Imposta il tempo di riposo della pompa"),
78887
+ phrase: t("Set the pump of {device} to rest {minutes} minutes per cycle", "Imposta la pompa di {device} a riposare {minutes} minuti per ciclo"),
78888
+ icon: "timer-off",
78889
+ openArgs: [{
78890
+ key: "minutes",
78891
+ label: t("Minutes", "Minuti"),
78892
+ offer: {
78893
+ kind: "number",
78894
+ min: 0,
78895
+ step: 1,
78896
+ unit: "min"
78897
+ }
78898
+ }]
78899
+ },
78900
+ {
78901
+ id: "petFountain.resetDailyConsumption",
78902
+ method: "resetDailyConsumption",
78903
+ label: t("Clear daily consumption", "Azzera il consumo giornaliero"),
78904
+ phrase: t("Clear the daily water consumption of {device}", "Azzera il consumo giornaliero di acqua di {device}"),
78905
+ icon: "rotate-ccw"
78906
+ }
78907
+ ],
78908
+ state: [{
78909
+ kind: "boolean",
78910
+ field: "empty",
78911
+ label: t("Tank empty", "Serbatoio vuoto"),
78912
+ whenTrue: t("When the tank of {device} is empty", "Quando il serbatoio di {device} è vuoto"),
78913
+ whenFalse: t("When the tank of {device} is refilled", "Quando il serbatoio di {device} viene riempito"),
78914
+ icon: "droplet"
78915
+ }, {
78916
+ kind: "numeric",
78917
+ field: "waterLevel",
78918
+ quantity: t("the water level", "il livello dell’acqua"),
78919
+ label: t("Water level", "Livello dell’acqua"),
78920
+ unit: "%",
78921
+ icon: "droplets"
78922
+ }]
78923
+ },
78544
78924
  "media-player": {
78545
78925
  scope: "device",
78546
78926
  noun: t("Media player", "Lettore multimediale"),
@@ -80365,6 +80745,7 @@ var SYSTEM_CAP_VOCABULARY = {
80365
80745
  "device-manager": system("the device registry itself — rename, move and remove are administration"),
80366
80746
  "device-provider": system(INTERNAL),
80367
80747
  "device-removal-hook": system(INTERNAL),
80748
+ "node-removal-hook": system(INTERNAL),
80368
80749
  "device-state": system("the runtime-state mirror every trigger already reads through"),
80369
80750
  "display-palette": system("the cluster display palette — one colour table for every client, not a device action"),
80370
80751
  "embedding-encoder": system(ML),
@@ -80477,7 +80858,7 @@ function projectAction(cap, action) {
80477
80858
  };
80478
80859
  }
80479
80860
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
80480
- DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
80861
+ DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
80481
80862
  DeviceFeature.BatteryOperated;
80482
80863
  DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
80483
80864
  var HA_AS = "a power sensor (device_class power). Home Assistant has no laundry domain. running-state stays unexported until a null reading can be published as unavailable.";
@@ -82028,6 +82409,36 @@ Object.freeze({
82028
82409
  form: "single",
82029
82410
  optional: false
82030
82411
  }],
82412
+ "petFountain.getStatus": [{
82413
+ name: "deviceId",
82414
+ form: "single",
82415
+ optional: false
82416
+ }],
82417
+ "petFountain.resetDailyConsumption": [{
82418
+ name: "deviceId",
82419
+ form: "single",
82420
+ optional: false
82421
+ }],
82422
+ "petFountain.setPump": [{
82423
+ name: "deviceId",
82424
+ form: "single",
82425
+ optional: false
82426
+ }],
82427
+ "petFountain.setPumpInterval": [{
82428
+ name: "deviceId",
82429
+ form: "single",
82430
+ optional: false
82431
+ }],
82432
+ "petFountain.setPumpOffMinutes": [{
82433
+ name: "deviceId",
82434
+ form: "single",
82435
+ optional: false
82436
+ }],
82437
+ "petFountain.setPumpOnMinutes": [{
82438
+ name: "deviceId",
82439
+ form: "single",
82440
+ optional: false
82441
+ }],
82031
82442
  "pipelineAnalytics.addRetrainFrameFromRecording": [{
82032
82443
  name: "deviceId",
82033
82444
  form: "single",
@@ -84704,7 +85115,7 @@ var AdminUIAddon = class extends BaseAddon {
84704
85115
  capability: adminUiCapability,
84705
85116
  provider: {
84706
85117
  getStaticDir: async () => ({ staticDir: resolveAdminUiDistDir() }),
84707
- getVersion: async () => ({ version: "1.2.515" })
85118
+ getVersion: async () => ({ version: "1.2.517" })
84708
85119
  }
84709
85120
  }];
84710
85121
  }