@camstack/core 0.1.15 → 0.1.17

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 (382) hide show
  1. package/dist/addon/addon-api-factory.d.ts +36 -0
  2. package/dist/addon/addon-api-factory.d.ts.map +1 -0
  3. package/dist/addon-routes/addon-route-registry.d.ts +38 -0
  4. package/dist/addon-routes/addon-route-registry.d.ts.map +1 -0
  5. package/dist/auth/api-key-manager.d.ts +27 -0
  6. package/dist/auth/api-key-manager.d.ts.map +1 -0
  7. package/dist/auth/auth-manager.d.ts +47 -0
  8. package/dist/auth/auth-manager.d.ts.map +1 -0
  9. package/dist/auth/parse-record.d.ts +19 -0
  10. package/dist/auth/parse-record.d.ts.map +1 -0
  11. package/dist/auth/scoped-token-manager.d.ts +18 -0
  12. package/dist/auth/scoped-token-manager.d.ts.map +1 -0
  13. package/dist/auth/user-manager.d.ts +34 -0
  14. package/dist/auth/user-manager.d.ts.map +1 -0
  15. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.d.ts +54 -0
  16. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.d.ts.map +1 -0
  17. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +223 -217
  18. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js.map +1 -1
  19. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +216 -7
  20. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs.map +1 -1
  21. package/dist/builtins/addon-pages-aggregator/index.d.ts +2 -0
  22. package/dist/builtins/addon-pages-aggregator/index.d.ts.map +1 -0
  23. package/dist/builtins/addon-pages-aggregator/index.js +6 -221
  24. package/dist/builtins/addon-pages-aggregator/index.mjs +2 -9
  25. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.d.ts +33 -0
  26. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.d.ts.map +1 -0
  27. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +199 -197
  28. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js.map +1 -1
  29. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +192 -7
  30. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs.map +1 -1
  31. package/dist/builtins/addon-widgets-aggregator/index.d.ts +2 -0
  32. package/dist/builtins/addon-widgets-aggregator/index.d.ts.map +1 -0
  33. package/dist/builtins/addon-widgets-aggregator/index.js +6 -201
  34. package/dist/builtins/addon-widgets-aggregator/index.mjs +2 -9
  35. package/dist/builtins/alerts/alerts.addon.d.ts +82 -0
  36. package/dist/builtins/alerts/alerts.addon.d.ts.map +1 -0
  37. package/dist/builtins/alerts/alerts.addon.js +590 -430
  38. package/dist/builtins/alerts/alerts.addon.js.map +1 -1
  39. package/dist/builtins/alerts/alerts.addon.mjs +595 -7
  40. package/dist/builtins/alerts/alerts.addon.mjs.map +1 -1
  41. package/dist/builtins/alerts/index.d.ts +2 -0
  42. package/dist/builtins/alerts/index.d.ts.map +1 -0
  43. package/dist/builtins/alerts/index.js +3 -443
  44. package/dist/builtins/alerts/index.mjs +2 -8
  45. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.d.ts +8 -0
  46. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.d.ts.map +1 -0
  47. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.js +56 -0
  48. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.js.map +1 -0
  49. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.mjs +50 -0
  50. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.mjs.map +1 -0
  51. package/dist/builtins/auth-orchestrator/index.d.ts +2 -0
  52. package/dist/builtins/auth-orchestrator/index.d.ts.map +1 -0
  53. package/dist/builtins/auth-orchestrator/index.js +7 -0
  54. package/dist/builtins/auth-orchestrator/index.mjs +2 -0
  55. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts +148 -0
  56. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts.map +1 -0
  57. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +7639 -0
  58. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js.map +1 -0
  59. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +7627 -0
  60. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs.map +1 -0
  61. package/dist/builtins/backup-orchestrator/cron-helpers.d.ts +24 -0
  62. package/dist/builtins/backup-orchestrator/cron-helpers.d.ts.map +1 -0
  63. package/dist/builtins/backup-orchestrator/destination-policy.d.ts +73 -0
  64. package/dist/builtins/backup-orchestrator/destination-policy.d.ts.map +1 -0
  65. package/dist/builtins/backup-orchestrator/download-helpers.d.ts +13 -0
  66. package/dist/builtins/backup-orchestrator/download-helpers.d.ts.map +1 -0
  67. package/dist/builtins/backup-orchestrator/index.d.ts +3 -0
  68. package/dist/builtins/backup-orchestrator/index.d.ts.map +1 -0
  69. package/dist/builtins/backup-orchestrator/index.js +7 -0
  70. package/dist/builtins/backup-orchestrator/index.mjs +2 -0
  71. package/dist/builtins/backup-orchestrator/manifest-store.d.ts +78 -0
  72. package/dist/builtins/backup-orchestrator/manifest-store.d.ts.map +1 -0
  73. package/dist/builtins/console-logging/console-destination.d.ts +14 -0
  74. package/dist/builtins/console-logging/console-destination.d.ts.map +1 -0
  75. package/dist/builtins/console-logging/console-logging.addon.d.ts +26 -0
  76. package/dist/builtins/console-logging/console-logging.addon.d.ts.map +1 -0
  77. package/dist/builtins/console-logging/index.d.ts +4 -0
  78. package/dist/builtins/console-logging/index.d.ts.map +1 -0
  79. package/dist/builtins/console-logging/index.js +99 -235
  80. package/dist/builtins/console-logging/index.js.map +1 -1
  81. package/dist/builtins/console-logging/index.mjs +95 -9
  82. package/dist/builtins/console-logging/index.mjs.map +1 -1
  83. package/dist/builtins/device-manager/device-event-propagator.d.ts +27 -0
  84. package/dist/builtins/device-manager/device-event-propagator.d.ts.map +1 -0
  85. package/dist/builtins/device-manager/device-manager.addon.d.ts +259 -0
  86. package/dist/builtins/device-manager/device-manager.addon.d.ts.map +1 -0
  87. package/dist/builtins/device-manager/device-manager.addon.js +2125 -2127
  88. package/dist/builtins/device-manager/device-manager.addon.js.map +1 -1
  89. package/dist/builtins/device-manager/device-manager.addon.mjs +2145 -7
  90. package/dist/builtins/device-manager/device-manager.addon.mjs.map +1 -1
  91. package/dist/builtins/device-manager/index.d.ts +3 -0
  92. package/dist/builtins/device-manager/index.d.ts.map +1 -0
  93. package/dist/builtins/device-manager/index.js +6 -2156
  94. package/dist/builtins/device-manager/index.mjs +2 -10
  95. package/dist/builtins/hub-forwarder/hub-forwarder-destination.d.ts +45 -0
  96. package/dist/builtins/hub-forwarder/hub-forwarder-destination.d.ts.map +1 -0
  97. package/dist/builtins/hub-forwarder/hub-forwarder.addon.d.ts +16 -0
  98. package/dist/builtins/hub-forwarder/hub-forwarder.addon.d.ts.map +1 -0
  99. package/dist/builtins/hub-forwarder/index.d.ts +4 -0
  100. package/dist/builtins/hub-forwarder/index.d.ts.map +1 -0
  101. package/dist/builtins/hub-forwarder/index.js +150 -291
  102. package/dist/builtins/hub-forwarder/index.js.map +1 -1
  103. package/dist/builtins/hub-forwarder/index.mjs +145 -9
  104. package/dist/builtins/hub-forwarder/index.mjs.map +1 -1
  105. package/dist/builtins/local-auth/auth-schema.d.ts +12 -0
  106. package/dist/builtins/local-auth/auth-schema.d.ts.map +1 -0
  107. package/dist/builtins/local-auth/index.d.ts +2 -0
  108. package/dist/builtins/local-auth/index.d.ts.map +1 -0
  109. package/dist/builtins/local-auth/index.js +3 -623
  110. package/dist/builtins/local-auth/index.mjs +2 -8
  111. package/dist/builtins/local-auth/local-auth.addon.d.ts +17 -0
  112. package/dist/builtins/local-auth/local-auth.addon.d.ts.map +1 -0
  113. package/dist/builtins/local-auth/local-auth.addon.js +6861 -589
  114. package/dist/builtins/local-auth/local-auth.addon.js.map +1 -1
  115. package/dist/builtins/local-auth/local-auth.addon.mjs +6883 -7
  116. package/dist/builtins/local-auth/local-auth.addon.mjs.map +1 -1
  117. package/dist/builtins/local-network/index.d.ts +3 -0
  118. package/dist/builtins/local-network/index.d.ts.map +1 -0
  119. package/dist/builtins/local-network/index.js +9 -0
  120. package/dist/builtins/local-network/index.mjs +2 -0
  121. package/dist/builtins/local-network/local-network.addon.d.ts +110 -0
  122. package/dist/builtins/local-network/local-network.addon.d.ts.map +1 -0
  123. package/dist/builtins/local-network/local-network.addon.js +399 -0
  124. package/dist/builtins/local-network/local-network.addon.js.map +1 -0
  125. package/dist/builtins/local-network/local-network.addon.mjs +387 -0
  126. package/dist/builtins/local-network/local-network.addon.mjs.map +1 -0
  127. package/dist/builtins/mesh-orchestrator/index.d.ts +2 -0
  128. package/dist/builtins/mesh-orchestrator/index.d.ts.map +1 -0
  129. package/dist/builtins/mesh-orchestrator/index.js +7 -0
  130. package/dist/builtins/mesh-orchestrator/index.mjs +2 -0
  131. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.d.ts +9 -0
  132. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.d.ts.map +1 -0
  133. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.js +83 -0
  134. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.js.map +1 -0
  135. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.mjs +77 -0
  136. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.mjs.map +1 -0
  137. package/dist/builtins/native-metrics/index.d.ts +3 -0
  138. package/dist/builtins/native-metrics/index.d.ts.map +1 -0
  139. package/dist/builtins/native-metrics/native-metrics-provider.d.ts +49 -0
  140. package/dist/builtins/native-metrics/native-metrics-provider.d.ts.map +1 -0
  141. package/dist/builtins/native-metrics/native-metrics.addon.d.ts +74 -0
  142. package/dist/builtins/native-metrics/native-metrics.addon.d.ts.map +1 -0
  143. package/dist/builtins/native-metrics/native-metrics.addon.js +887 -861
  144. package/dist/builtins/native-metrics/native-metrics.addon.js.map +1 -1
  145. package/dist/builtins/native-metrics/native-metrics.addon.mjs +914 -5
  146. package/dist/builtins/native-metrics/native-metrics.addon.mjs.map +1 -1
  147. package/dist/builtins/platform-probe/index.d.ts +12 -0
  148. package/dist/builtins/platform-probe/index.d.ts.map +1 -0
  149. package/dist/builtins/platform-probe/index.js +539 -0
  150. package/dist/builtins/platform-probe/index.js.map +1 -0
  151. package/dist/builtins/platform-probe/index.mjs +530 -0
  152. package/dist/builtins/platform-probe/index.mjs.map +1 -0
  153. package/dist/builtins/platform-probe/inference-config-resolver.d.ts +30 -0
  154. package/dist/builtins/platform-probe/inference-config-resolver.d.ts.map +1 -0
  155. package/dist/builtins/platform-probe/platform-scorer.d.ts +22 -0
  156. package/dist/builtins/platform-probe/platform-scorer.d.ts.map +1 -0
  157. package/dist/builtins/remote-access-orchestrator/index.d.ts +2 -0
  158. package/dist/builtins/remote-access-orchestrator/index.d.ts.map +1 -0
  159. package/dist/builtins/remote-access-orchestrator/index.js +7 -0
  160. package/dist/builtins/remote-access-orchestrator/index.mjs +2 -0
  161. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.d.ts +9 -0
  162. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.d.ts.map +1 -0
  163. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +72 -0
  164. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js.map +1 -0
  165. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +66 -0
  166. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs.map +1 -0
  167. package/dist/builtins/snapshot/index.d.ts +3 -0
  168. package/dist/builtins/snapshot/index.d.ts.map +1 -0
  169. package/dist/builtins/snapshot/index.js +481 -491
  170. package/dist/builtins/snapshot/index.js.map +1 -1
  171. package/dist/builtins/snapshot/index.mjs +475 -464
  172. package/dist/builtins/snapshot/index.mjs.map +1 -1
  173. package/dist/builtins/snapshot/snapshot.addon.d.ts +121 -0
  174. package/dist/builtins/snapshot/snapshot.addon.d.ts.map +1 -0
  175. package/dist/builtins/sqlite-storage/config-store.d.ts +9 -0
  176. package/dist/builtins/sqlite-storage/config-store.d.ts.map +1 -0
  177. package/dist/builtins/sqlite-storage/device-store.d.ts +24 -0
  178. package/dist/builtins/sqlite-storage/device-store.d.ts.map +1 -0
  179. package/dist/builtins/sqlite-storage/filesystem-storage-provider.d.ts +87 -0
  180. package/dist/builtins/sqlite-storage/filesystem-storage-provider.d.ts.map +1 -0
  181. package/dist/builtins/sqlite-storage/filesystem-storage.addon.d.ts +32 -0
  182. package/dist/builtins/sqlite-storage/filesystem-storage.addon.d.ts.map +1 -0
  183. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +312 -56
  184. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js.map +1 -1
  185. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +305 -7
  186. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs.map +1 -1
  187. package/dist/builtins/sqlite-storage/index.d.ts +12 -0
  188. package/dist/builtins/sqlite-storage/index.d.ts.map +1 -0
  189. package/dist/builtins/sqlite-storage/index.js +229 -1001
  190. package/dist/builtins/sqlite-storage/index.js.map +1 -1
  191. package/dist/builtins/sqlite-storage/index.mjs +268 -26
  192. package/dist/builtins/sqlite-storage/index.mjs.map +1 -1
  193. package/dist/builtins/sqlite-storage/integration-registry.d.ts +28 -0
  194. package/dist/builtins/sqlite-storage/integration-registry.d.ts.map +1 -0
  195. package/dist/builtins/sqlite-storage/settings-store.d.ts +40 -0
  196. package/dist/builtins/sqlite-storage/settings-store.d.ts.map +1 -0
  197. package/dist/builtins/sqlite-storage/sql-schema.d.ts +33 -0
  198. package/dist/builtins/sqlite-storage/sql-schema.d.ts.map +1 -0
  199. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +94 -0
  200. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts.map +1 -0
  201. package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +15 -0
  202. package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts.map +1 -0
  203. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +586 -653
  204. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js.map +1 -1
  205. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +582 -7
  206. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs.map +1 -1
  207. package/dist/builtins/storage-orchestrator/index.d.ts +7 -0
  208. package/dist/builtins/storage-orchestrator/index.d.ts.map +1 -0
  209. package/dist/builtins/storage-orchestrator/index.js +9 -0
  210. package/dist/builtins/storage-orchestrator/index.mjs +2 -0
  211. package/dist/builtins/storage-orchestrator/location-store.d.ts +50 -0
  212. package/dist/builtins/storage-orchestrator/location-store.d.ts.map +1 -0
  213. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +60 -0
  214. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts.map +1 -0
  215. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +755 -0
  216. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js.map +1 -0
  217. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +746 -0
  218. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs.map +1 -0
  219. package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +121 -0
  220. package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts.map +1 -0
  221. package/dist/builtins/system-backup/system-backup.service.d.ts +138 -0
  222. package/dist/builtins/system-backup/system-backup.service.d.ts.map +1 -0
  223. package/dist/builtins/system-config/index.d.ts +2 -0
  224. package/dist/builtins/system-config/index.d.ts.map +1 -0
  225. package/dist/builtins/system-config/index.js +6 -188
  226. package/dist/builtins/system-config/index.mjs +2 -10
  227. package/dist/builtins/system-config/system-config.addon.d.ts +11 -0
  228. package/dist/builtins/system-config/system-config.addon.d.ts.map +1 -0
  229. package/dist/builtins/system-config/system-config.addon.js +227 -180
  230. package/dist/builtins/system-config/system-config.addon.js.map +1 -1
  231. package/dist/builtins/system-config/system-config.addon.mjs +226 -7
  232. package/dist/builtins/system-config/system-config.addon.mjs.map +1 -1
  233. package/dist/builtins/turn-orchestrator/index.d.ts +2 -0
  234. package/dist/builtins/turn-orchestrator/index.d.ts.map +1 -0
  235. package/dist/builtins/turn-orchestrator/index.js +7 -0
  236. package/dist/builtins/turn-orchestrator/index.mjs +2 -0
  237. package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.d.ts +10 -0
  238. package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.d.ts.map +1 -0
  239. package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.js +78 -0
  240. package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.js.map +1 -0
  241. package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.mjs +72 -0
  242. package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.mjs.map +1 -0
  243. package/dist/builtins/winston-logging/index.d.ts +4 -0
  244. package/dist/builtins/winston-logging/index.d.ts.map +1 -0
  245. package/dist/builtins/winston-logging/index.js +153 -300
  246. package/dist/builtins/winston-logging/index.js.map +1 -1
  247. package/dist/builtins/winston-logging/index.mjs +144 -9
  248. package/dist/builtins/winston-logging/index.mjs.map +1 -1
  249. package/dist/builtins/winston-logging/winston-destination.d.ts +22 -0
  250. package/dist/builtins/winston-logging/winston-destination.d.ts.map +1 -0
  251. package/dist/builtins/winston-logging/winston-logging.addon.d.ts +20 -0
  252. package/dist/builtins/winston-logging/winston-logging.addon.d.ts.map +1 -0
  253. package/dist/chunk-C13QxCFV.js +50 -0
  254. package/dist/chunk-hT5z_Zn9.mjs +35 -0
  255. package/dist/download/model-download-service.d.ts +42 -0
  256. package/dist/download/model-download-service.d.ts.map +1 -0
  257. package/dist/download/model-downloader.d.ts +32 -0
  258. package/dist/download/model-downloader.d.ts.map +1 -0
  259. package/dist/events/event-bus.d.ts +11 -0
  260. package/dist/events/event-bus.d.ts.map +1 -0
  261. package/dist/events/system-event-bus.d.ts +15 -0
  262. package/dist/events/system-event-bus.d.ts.map +1 -0
  263. package/dist/feature/feature-manager.d.ts +12 -0
  264. package/dist/feature/feature-manager.d.ts.map +1 -0
  265. package/dist/formatter-C-5An4Bl.mjs +164 -0
  266. package/dist/formatter-C-5An4Bl.mjs.map +1 -0
  267. package/dist/formatter-Dr_6NNZc.js +169 -0
  268. package/dist/formatter-Dr_6NNZc.js.map +1 -0
  269. package/dist/index.d.ts +76 -1696
  270. package/dist/index.d.ts.map +1 -0
  271. package/dist/index.js +7780 -8035
  272. package/dist/index.js.map +1 -1
  273. package/dist/index.mjs +7707 -2148
  274. package/dist/index.mjs.map +1 -1
  275. package/dist/lifecycle/lifecycle-state-machine.d.ts +29 -0
  276. package/dist/lifecycle/lifecycle-state-machine.d.ts.map +1 -0
  277. package/dist/logging/formatter.d.ts +31 -0
  278. package/dist/logging/formatter.d.ts.map +1 -0
  279. package/dist/logging/log-manager.d.ts +52 -0
  280. package/dist/logging/log-manager.d.ts.map +1 -0
  281. package/dist/logging/log-ring-buffer.d.ts +48 -0
  282. package/dist/logging/log-ring-buffer.d.ts.map +1 -0
  283. package/dist/logging/scoped-logger.d.ts +18 -0
  284. package/dist/logging/scoped-logger.d.ts.map +1 -0
  285. package/dist/network/network-quality.d.ts +12 -0
  286. package/dist/network/network-quality.d.ts.map +1 -0
  287. package/dist/notification/notification-service.d.ts +38 -0
  288. package/dist/notification/notification-service.d.ts.map +1 -0
  289. package/dist/notification/toast-service.d.ts +23 -0
  290. package/dist/notification/toast-service.d.ts.map +1 -0
  291. package/dist/pipeline/engine-manager-resolver.d.ts +16 -0
  292. package/dist/pipeline/engine-manager-resolver.d.ts.map +1 -0
  293. package/dist/pipeline/pipeline-runner.d.ts +9 -0
  294. package/dist/pipeline/pipeline-runner.d.ts.map +1 -0
  295. package/dist/pipeline/pipeline-validator.d.ts +14 -0
  296. package/dist/pipeline/pipeline-validator.d.ts.map +1 -0
  297. package/dist/process/resource-monitor.d.ts +12 -0
  298. package/dist/process/resource-monitor.d.ts.map +1 -0
  299. package/dist/python/python-env-manager.d.ts +13 -0
  300. package/dist/python/python-env-manager.d.ts.map +1 -0
  301. package/dist/repl/interfaces.d.ts +32 -0
  302. package/dist/repl/interfaces.d.ts.map +1 -0
  303. package/dist/repl/repl-engine.d.ts +9 -0
  304. package/dist/repl/repl-engine.d.ts.map +1 -0
  305. package/dist/resource-monitor-CmuWlmap.js +76 -0
  306. package/dist/resource-monitor-CmuWlmap.js.map +1 -0
  307. package/dist/resource-monitor-DcQdKGYU.mjs +59 -0
  308. package/dist/resource-monitor-DcQdKGYU.mjs.map +1 -0
  309. package/dist/storage/fs-storage-backend.d.ts +41 -0
  310. package/dist/storage/fs-storage-backend.d.ts.map +1 -0
  311. package/dist/storage/storage-location-manager.d.ts +24 -0
  312. package/dist/storage/storage-location-manager.d.ts.map +1 -0
  313. package/dist/storage/storage-manager.d.ts +77 -0
  314. package/dist/storage/storage-manager.d.ts.map +1 -0
  315. package/dist/tls/cert-manager.d.ts +27 -0
  316. package/dist/tls/cert-manager.d.ts.map +1 -0
  317. package/dist/tls/index.d.ts +2 -0
  318. package/dist/tls/index.d.ts.map +1 -0
  319. package/package.json +119 -23
  320. package/dist/builtins/addon-pages-aggregator/index.js.map +0 -1
  321. package/dist/builtins/addon-pages-aggregator/index.mjs.map +0 -1
  322. package/dist/builtins/addon-widgets-aggregator/index.js.map +0 -1
  323. package/dist/builtins/addon-widgets-aggregator/index.mjs.map +0 -1
  324. package/dist/builtins/alerts/index.js.map +0 -1
  325. package/dist/builtins/alerts/index.mjs.map +0 -1
  326. package/dist/builtins/device-manager/index.js.map +0 -1
  327. package/dist/builtins/device-manager/index.mjs.map +0 -1
  328. package/dist/builtins/local-auth/index.js.map +0 -1
  329. package/dist/builtins/local-auth/index.mjs.map +0 -1
  330. package/dist/builtins/local-backup/index.js +0 -173
  331. package/dist/builtins/local-backup/index.js.map +0 -1
  332. package/dist/builtins/local-backup/index.mjs +0 -10
  333. package/dist/builtins/local-backup/index.mjs.map +0 -1
  334. package/dist/builtins/system-config/index.js.map +0 -1
  335. package/dist/builtins/system-config/index.mjs.map +0 -1
  336. package/dist/chunk-2CIYKDRN.mjs +0 -1
  337. package/dist/chunk-2CIYKDRN.mjs.map +0 -1
  338. package/dist/chunk-2F76X6NL.mjs +0 -136
  339. package/dist/chunk-2F76X6NL.mjs.map +0 -1
  340. package/dist/chunk-2QUFBZ7M.mjs +0 -1
  341. package/dist/chunk-2QUFBZ7M.mjs.map +0 -1
  342. package/dist/chunk-3BK2Y7GY.mjs +0 -593
  343. package/dist/chunk-3BK2Y7GY.mjs.map +0 -1
  344. package/dist/chunk-4OOHFJHT.mjs +0 -421
  345. package/dist/chunk-4OOHFJHT.mjs.map +0 -1
  346. package/dist/chunk-4XHB7IHT.mjs +0 -809
  347. package/dist/chunk-4XHB7IHT.mjs.map +0 -1
  348. package/dist/chunk-6M2HSSTQ.mjs +0 -98
  349. package/dist/chunk-6M2HSSTQ.mjs.map +0 -1
  350. package/dist/chunk-7FI7SQS7.mjs +0 -135
  351. package/dist/chunk-7FI7SQS7.mjs.map +0 -1
  352. package/dist/chunk-ED57RCQE.mjs +0 -171
  353. package/dist/chunk-ED57RCQE.mjs.map +0 -1
  354. package/dist/chunk-FZN56HGQ.mjs +0 -626
  355. package/dist/chunk-FZN56HGQ.mjs.map +0 -1
  356. package/dist/chunk-GL4OOB25.mjs +0 -51
  357. package/dist/chunk-GL4OOB25.mjs.map +0 -1
  358. package/dist/chunk-KDG2NTDB.mjs +0 -137
  359. package/dist/chunk-KDG2NTDB.mjs.map +0 -1
  360. package/dist/chunk-NRBQWBDM.mjs +0 -191
  361. package/dist/chunk-NRBQWBDM.mjs.map +0 -1
  362. package/dist/chunk-O4V246GG.mjs +0 -2137
  363. package/dist/chunk-O4V246GG.mjs.map +0 -1
  364. package/dist/chunk-QT57H266.mjs +0 -163
  365. package/dist/chunk-QT57H266.mjs.map +0 -1
  366. package/dist/chunk-QX4RH25I.mjs +0 -141
  367. package/dist/chunk-QX4RH25I.mjs.map +0 -1
  368. package/dist/chunk-TB562PZX.mjs +0 -86
  369. package/dist/chunk-TB562PZX.mjs.map +0 -1
  370. package/dist/chunk-TDYPZXK5.mjs +0 -1
  371. package/dist/chunk-TDYPZXK5.mjs.map +0 -1
  372. package/dist/chunk-UJI4LN5P.mjs +0 -36
  373. package/dist/chunk-UJI4LN5P.mjs.map +0 -1
  374. package/dist/chunk-W6RTHQGP.mjs +0 -1
  375. package/dist/chunk-W6RTHQGP.mjs.map +0 -1
  376. package/dist/chunk-ZELBCPDC.mjs +0 -369
  377. package/dist/chunk-ZELBCPDC.mjs.map +0 -1
  378. package/dist/index.d.mts +0 -1696
  379. package/dist/resource-monitor-UZUGPIAU.mjs +0 -9
  380. package/dist/resource-monitor-UZUGPIAU.mjs.map +0 -1
  381. package/dist/storage-location-manager-HFNB3PCS.mjs +0 -7
  382. package/dist/storage-location-manager-HFNB3PCS.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/builtins/device-manager/device-manager.addon.ts","../src/builtins/device-manager/device-event-propagator.ts"],"sourcesContent":["/**\n * Device Manager addon — hub-side singleton that unifies device persistence,\n * live registry queries, and all management operations into a single\n * tRPC-routable capability.\n *\n * Persistence strategy: all device data is stored via `ctx.settings`, the same\n * settings API every other addon uses. No raw SQLite access.\n *\n * Addon store layout:\n * deviceIndex → Record<addonId, stableId[]> (which devices exist per addon)\n * deviceMeta → Record<\"addonId:stableId\", DeviceMeta> (type, name, parentDeviceId, id)\n *\n * Device store (per-device config):\n * readDeviceStore(numericDeviceId) → config blob\n * writeDeviceStore(numericDeviceId, patch)\n *\n * Live registry: resolved from the kernel capability registry after Phase 2.\n * This gives direct access to in-memory IDevice instances registered by provider addons.\n * The DeviceManagerAddon is the single owner of the live device operations API.\n *\n * Replaces:\n * - `device-persistence` capability (absorbed here)\n * - live operations previously served by `device-management.router.ts`\n */\nimport { randomUUID } from 'node:crypto'\nimport { EventCategory,\n ProviderRegistration,\n BaseAddon,\n DeviceType,\n DeviceFeature,\n errMsg,\n} from '@camstack/types'\nimport { DeviceEventPropagator } from './device-event-propagator.js'\nimport { CAP_NAMES_WITH_STATUS } from '@camstack/types'\nimport { WELL_KNOWN_TAB_MAP } from '@camstack/types'\nimport type {\n IDevice,\n ICameraDevice,\n StreamSourceEntry,\n DeviceSettingsContribution,\n ConfigUISchemaWithValues,\n} from '@camstack/types'\n\n/**\n * Wire shape matching `z.infer<typeof SettingsSchemaWithValuesSchema>` —\n * duplicated as a plain interface because importing the Zod schema across\n * package boundaries confuses `tsc` when the types package pins a\n * different `zod` minor than the core package. Keeping the shape local\n * keeps the addon decoupled from the schema's internal type encoding.\n */\ninterface ContributionShape {\n tabs?: Array<{ id: string; label: string; icon: string; order?: number }>\n sections: Array<{\n id: string\n title: string\n description?: string\n style?: 'card' | 'accordion'\n defaultCollapsed?: boolean\n columns?: 1 | 2 | 3 | 4\n tab?: string\n /** Where the section renders. Default 'settings' (Config tab); 'top-tab' hoists into the device-detail tab bar via DeviceDetail discovery. */\n location?: 'settings' | 'top-tab'\n order?: number\n fields: unknown[]\n }>\n}\nimport { deviceManagerCapability, deviceProviderCapability, deviceStateCapability } from '@camstack/types'\nimport type { InferProvider, IDeviceOpsProvider, ICapabilityRegistry } from '@camstack/types'\nimport type { IDeviceRegistry } from '@camstack/types'\nimport type { DeviceBindingEntry } from '@camstack/types'\n\ntype IDeviceManagerProvider = InferProvider<typeof deviceManagerCapability>\ntype IDeviceProviderCap = InferProvider<typeof deviceProviderCapability>\n\n// ── Bindings store types ─────────────────────────────────────────────────────\n\ninterface DeviceBindingsStore {\n readonly deviceBindings: Record<string /* deviceId */, Record<string /* capName */, { wrapperAddonId: string | null }>>\n}\n\ninterface PersistedDeviceMeta {\n type: string\n name: string\n /**\n * Operator-organisational label (room / area / zone). `null` when\n * unset. Mutable through `setLocation` action; surfaced on\n * `IDevice.location` via the kernel-built `DeviceContext.deviceMeta`\n * snapshot. Free-text — providers don't interpret it.\n */\n location: string | null\n /**\n * Soft-disabled flag. When `true`, the device class is still\n * instantiated and visible in the UI (so the operator can flip\n * back on without re-adding) but lifecycle hooks are gated by\n * `BaseDevice.disabled`. Mutable through `setDisabled` action.\n */\n disabled: boolean\n /** Numeric parent id (or null for standalone). The hub resolves the\n * parent's stableId to its numeric id at registration time, and from\n * there everything internal references the parent by `parentDeviceId`\n * — `stableId` only crosses the external boundary (autodiscovery and\n * `(addonId, stableId)` row lookups). */\n parentDeviceId: number | null\n /** Progressive, system-wide unique id. Allocated through\n * `allocateDeviceId` before `registerDevice` runs; backfilled at boot\n * for any legacy meta entry missing it (see init migration below).\n * Post-init invariant: every persisted device has a numeric id. */\n id: number\n /** Snapshot of `device.features` from the most recent `registerDevice`\n * call. Used by `getDevice` when the live `IDevice` instance lives\n * on a forked-worker / remote agent and isn't reachable from the\n * hub registry. Optional for legacy rows that predate the field —\n * callers fall back to an empty array. */\n features?: readonly string[]\n}\n\ninterface AddonStore {\n deviceIndex?: Record<string, string[]>\n deviceMeta?: Record<string, PersistedDeviceMeta>\n /** Hardware-identity metadata blob keyed by `<addonId>:<stableId>`.\n * Stored in a separate key from `deviceMeta` so the lifecycle\n * writers (registerDevice / setName / setLocation / setDisabled)\n * never need to read or preserve the metadata field — single-writer\n * per row eliminates the \"writer X clobbers writer Y's field\" bug\n * class. The `metadata` field on `PersistedDeviceMeta` is a legacy\n * fallback consulted only at read time during the lazy migration —\n * new writes always go to `deviceMetadata`. */\n deviceMetadata?: Record<string, Record<string, unknown>>\n /** Monotonic counter that feeds `PersistedDeviceMeta.id` for every\n * new device. Incremented exactly once per registerDevice; never\n * decremented. */\n nextDeviceId?: number\n /**\n * Operator-curated location registry. Each entry is a free-form room/\n * area label (e.g. \"Cucina\", \"Garage\") that drives the\n * autocomplete in the Device Info location editor. Devices reference\n * these as plain strings via `PersistedDeviceMeta.location` — the\n * registry is purely a suggestion list, NOT a foreign key. Removing a\n * location from the registry leaves devices that still reference it\n * intact; `listLocations` returns the union of registered labels +\n * labels currently in use, so nothing disappears from the UI even if\n * the operator forgot to register a label before assigning it.\n */\n locations?: readonly string[]\n}\n\nfunction shallowEqual(a: Record<string, unknown>, b: Record<string, unknown>): boolean {\n const ak = Object.keys(a)\n const bk = Object.keys(b)\n if (ak.length !== bk.length) return false\n for (const k of ak) {\n if (a[k] !== b[k]) return false\n }\n return true\n}\n\nfunction deviceKey(addonId: string, stableId: string): string {\n return `${addonId}:${stableId}`\n}\n\n// ── Type guards ──────────────────────────────────────────────────────────────\n\nfunction isCameraDevice(device: IDevice): device is ICameraDevice {\n return 'getStreamSources' in device && typeof (device as ICameraDevice).getStreamSources === 'function'\n}\n\nconst DEVICE_FEATURE_VALUES = new Set<string>(Object.values(DeviceFeature) as readonly string[])\n\n/**\n * Validate persisted feature strings against the `DeviceFeature` enum\n * — workers serialise the live `device.features` array (so every entry\n * is a valid enum value at write time) but the persisted blob is loose\n * `string[]` on the wire. The narrow keeps unknown values out of the\n * `getDevice` response without losing the enum-typed contract.\n */\nfunction persistedFeatures(features: readonly string[] | undefined): DeviceFeature[] {\n if (!features) return []\n const out: DeviceFeature[] = []\n for (const f of features) {\n if (DEVICE_FEATURE_VALUES.has(f)) out.push(f as DeviceFeature)\n }\n return out\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction toDeviceInfo(\n addonId: string,\n device: IDevice,\n metadata: Record<string, unknown> | null = null,\n metaRow: PersistedDeviceMeta | null = null,\n) {\n const configValues: Record<string, unknown> = {}\n for (const entry of device.config.entries()) {\n configValues[entry.key] = entry.value\n }\n // Operator-organisational fields (`name` / `location` / `disabled`)\n // are mutated through `setName` / `setLocation` / `setDisabled`,\n // which write to the persisted `deviceMeta` row and emit\n // `DeviceMetaChanged`. The live `BaseDevice` instance reads these\n // from `ctx.deviceMeta` at CONSTRUCTION ONLY (see\n // `base-device.ts:140-142`), so its `device.name` / `device.location`\n // / `device.disabled` fields are stale until the device class is\n // re-instantiated. Reading from `metaRow` here guarantees the wire\n // response reflects the latest persisted value within one settings\n // round-trip — the UI's enable/disable toggle (and rename) flips\n // immediately after the mutation completes. Falls back to the live\n // device fields when no meta row was supplied (test fixtures, edge\n // paths) so existing behaviour is preserved.\n const name = metaRow?.name ?? device.name\n const location = metaRow?.location !== undefined ? metaRow.location : device.location\n const disabled = metaRow?.disabled ?? device.disabled\n return {\n // Progressive system-wide id — bound on the live `IDevice` at\n // construction time (see `DeviceContext.id` / `BaseDevice.id`). The\n // hub allocates via `allocateDeviceId` BEFORE the device is\n // instantiated, so every live device exposes one.\n id: device.id,\n stableId: device.stableId,\n addonId,\n type: device.type,\n name,\n location,\n disabled,\n parentDeviceId: device.parentDeviceId,\n // `role` is set by the provider on accessory children (synthesized\n // via `getAccessoryChildren()` — Reolink siren/floodlight/PIR,\n // Hikvision supplemental light/siren). Top-level devices and\n // first-class hub-adopted children leave it `undefined`. Surfacing\n // it on `DeviceInfo` lets the UI distinguish accessory rows from\n // adopted-camera rows without re-deriving the heuristic from the\n // provider's `deviceClasses` map.\n role: device.role ?? null,\n online: device.online,\n features: [...device.features],\n isCamera: isCameraDevice(device),\n config: configValues,\n metadata,\n }\n}\n\nfunction resolveDeviceById(\n registry: IDeviceRegistry,\n deviceId: number,\n): { addonId: string; device: IDevice } | null {\n const device = registry.getById(deviceId)\n if (!device) return null\n const addonId = registry.getAddonId(deviceId)\n if (!addonId) return null\n return { addonId, device }\n}\n\n// ── Aggregator merge ─────────────────────────────────────────────────────────\n//\n// Concatenate several contributions into a single wire-shape object matching\n// `z.infer<typeof SettingsSchemaWithValuesSchema>` (plain mutable arrays).\n// Tabs with the same `id` collapse into one; sections within a tab are kept\n// in contribution order (contributors self-sort via `order`).\n//\n// No field-level merge — if two providers target the same `(tab, section)`\n// they're concatenated in discovery order. This is intentional: letting the\n// aggregator pick a winner would mask accidental collisions and silently\n// drop fields.\n//\n// `ContributionShape` / `AggregateShape` mirror the cap output exactly so no\n// assertion is needed when returning from the device-manager handlers.\n\ntype AggregateTabDecl = NonNullable<ContributionShape['tabs']>[number]\ntype AggregateSection = ContributionShape['sections'][number]\n\n/**\n * Walk the sections/fields of a contribution and inject `writerCapName` +\n * `writerAddonId` + `source` on each editable field. Readonly fields and\n * structural fields (separator/info/button) pass through untouched. The\n * aggregator is the single place that knows provenance — provider schemas\n * stay clean, UI-bound metadata is attached once at the boundary.\n */\nfunction tagContribution(\n contribution: ContributionShape,\n capName: string,\n addonId: string,\n kind: 'settings' | 'live',\n): ContributionShape {\n const source = kind === 'settings' ? 'settings' : 'live'\n return {\n ...(contribution.tabs ? { tabs: [...contribution.tabs] } : {}),\n sections: contribution.sections.map(section => ({\n ...section,\n fields: section.fields.map(field => tagField(field, capName, addonId, source, kind)),\n })),\n }\n}\n\nfunction isFieldRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n}\n\n/**\n * Convert a strict `ConfigUISchemaWithValues` (readonly arrays, typed\n * field union) into the cap wire shape `ContributionShape` (mutable\n * arrays, opaque field records). Required because the cap method z.infer\n * uses mutable arrays — readonly arrays are not assignable to mutable\n * even when structurally identical, so a structural copy bridges the gap\n * without disabling the type checker.\n */\nfunction toWireShape(input: ConfigUISchemaWithValues): ContributionShape {\n const out: ContributionShape = {\n sections: input.sections.map(s => ({\n id: s.id,\n title: s.title,\n ...(s.description !== undefined ? { description: s.description } : {}),\n ...(s.style !== undefined ? { style: s.style } : {}),\n ...(s.defaultCollapsed !== undefined ? { defaultCollapsed: s.defaultCollapsed } : {}),\n ...(s.columns !== undefined ? { columns: s.columns } : {}),\n ...(s.tab !== undefined ? { tab: s.tab } : {}),\n // `location: 'top-tab'` hoists the section into a discovered top-\n // level device tab in DeviceDetail. Forgetting to forward it here\n // strips the marker before the client sees it, so the tab never\n // shows up — see `DeviceDetail.tsx` aggregate-driven tab discovery.\n ...(s.location !== undefined ? { location: s.location } : {}),\n ...(s.order !== undefined ? { order: s.order } : {}),\n fields: [...s.fields],\n })),\n }\n if (input.tabs) out.tabs = [...input.tabs]\n return out\n}\n\nfunction tagField(\n field: unknown,\n capName: string,\n addonId: string,\n source: 'settings' | 'live',\n kind: 'settings' | 'live',\n): unknown {\n if (!isFieldRecord(field)) return field\n const f = field\n const structuralTypes = new Set(['separator', 'info', 'button'])\n if (typeof f.type === 'string' && structuralTypes.has(f.type)) return field\n\n const tagged: Record<string, unknown> = { ...f, source }\n if (kind === 'live' || f.readonlyField === true) {\n tagged.readonlyField = true\n } else {\n tagged.writerCapName = capName\n tagged.writerAddonId = addonId\n }\n\n // Recurse into composite layouts so nested leaf fields carry the\n // writer routing too. Without this, fields buried inside `'group'`\n // or `'sub-tabs'` containers are surfaced to the UI without a\n // writerCapName/writerAddonId — `findWriter` then can't route the\n // patch back to the cap that produced the field. Records are\n // typed `Record<string, unknown>`, so each step needs an explicit\n // `unknown[]` narrow before `.map` to keep the no-`any` rule happy.\n if (f.type === 'group') {\n const children: readonly unknown[] = Array.isArray(f.fields) ? f.fields : []\n if (children.length > 0) {\n tagged.fields = children.map((child) => tagField(child, capName, addonId, source, kind))\n }\n } else if (f.type === 'sub-tabs') {\n const rawTabs: readonly unknown[] = Array.isArray(f.tabs) ? f.tabs : []\n if (rawTabs.length > 0) {\n tagged.tabs = rawTabs.map((tab) => {\n if (!isFieldRecord(tab)) return tab\n const tabChildren: readonly unknown[] = Array.isArray(tab.fields) ? tab.fields : []\n return {\n ...tab,\n fields: tabChildren.map((child) => tagField(child, capName, addonId, source, kind)),\n }\n })\n }\n }\n return tagged\n}\n\nfunction mergeAggregates(parts: readonly ContributionShape[]): ContributionShape {\n const tabDecls = new Map<string, AggregateTabDecl>()\n const sections: AggregateSection[] = []\n\n for (const part of parts) {\n if (part.tabs) {\n for (const t of part.tabs) {\n if (!tabDecls.has(t.id)) tabDecls.set(t.id, t)\n }\n }\n for (const s of part.sections) {\n sections.push(s)\n }\n }\n\n // Auto-derive tab declarations from section references when contributors\n // don't emit one explicitly. Every tab id referenced in a section's `tab`\n // field gets a declaration — if the id matches a well-known tab\n // (general/detection/streaming/recording/…) the canonical label+icon+order\n // is used; otherwise we fall back to the raw id as label with a generic\n // icon. Ensures the UI has everything it needs to render the tab bar\n // from a single payload — no extra lookup, no duplicate source of truth.\n for (const s of sections) {\n const tabId = s.tab ?? 'general'\n if (tabDecls.has(tabId)) continue\n const known = WELL_KNOWN_TAB_MAP[tabId as keyof typeof WELL_KNOWN_TAB_MAP]\n if (known) {\n tabDecls.set(tabId, { id: known.id, label: known.label, icon: known.icon, order: known.order })\n } else {\n tabDecls.set(tabId, { id: tabId, label: tabId, icon: 'wrench', order: 100 })\n }\n }\n\n sections.sort((a, b) => {\n const tabA = a.tab ?? 'general'\n const tabB = b.tab ?? 'general'\n if (tabA !== tabB) {\n const orderA = tabDecls.get(tabA)?.order ?? 100\n const orderB = tabDecls.get(tabB)?.order ?? 100\n if (orderA !== orderB) return orderA - orderB\n return tabA.localeCompare(tabB)\n }\n return (a.order ?? 0) - (b.order ?? 0)\n })\n\n const sortedTabs = [...tabDecls.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100))\n const out: ContributionShape = { sections }\n if (sortedTabs.length > 0) out.tabs = sortedTabs\n return out\n}\n\nexport class DeviceManagerAddon extends BaseAddon {\n constructor() { super({}) }\n\n /** Shorthand for the kernel-injected capability registry. */\n private get capabilityRegistry(): ICapabilityRegistry | undefined {\n return this.ctx.kernel.capabilityRegistry\n }\n\n /**\n * Parent-chain event propagator. Started in `onInitialize` once the\n * hub's `deviceRegistry` is available; listens to every device-sourced\n * event and re-emits a copy on each ancestor scope with `via[]`\n * populated. Stopped in `onShutdown`.\n */\n private propagator: DeviceEventPropagator | null = null\n /**\n * Hub-side mirror of every device's cap-keyed runtime state.\n * Populated whenever any caller writes via `deviceState.setCapSlice`\n * (the canonical cross-layer write entrypoint) and on first load\n * via `loadRuntimeState`. Cross-process consumers reach the mirror\n * through the `deviceState` cap router; per-cap event subscribers\n * (e.g. `battery.onStatusChanged`) get the same data via\n * cap-specific events still emitted by the owning device.\n *\n * Key: deviceId. Value: per-cap slice map. Empty by default —\n * slices show up as `setCapSlice` calls trickle in.\n */\n private readonly stateMirror = new Map<number, Map<string, Record<string, unknown>>>()\n\n /**\n * Per-device disk-write debouncer for runtime-state. `setCapSlice`\n * updates the in-memory mirror synchronously and emits the change\n * event immediately, but the disk write is coalesced — frequent\n * back-to-back writes (motion phase transitions, battery pushes,\n * etc.) collapse to one `writeDeviceRuntimeState` per\n * `RUNTIME_STATE_DEBOUNCE_MS` window. `flushRuntimeStateWrites`\n * awaits any in-flight write + scheduled flush so shutdown is\n * lossless.\n */\n private readonly runtimeStateDebounce = new Map<number, {\n timer: ReturnType<typeof setTimeout> | null\n inFlight: Promise<void> | null\n }>()\n private static readonly RUNTIME_STATE_DEBOUNCE_MS = 1000\n\n /**\n * Cross-process native-provider cache: deviceId (numeric) → capName → { addonId, nodeId }.\n * Kept in sync with `device.bindings-changed` events emitted by forked\n * workers. Union'd into `getBindings` so hub-side consumers see every\n * native cap regardless of which process owns the IDevice. No persistence\n * — entries re-register when the worker restarts. Purged on\n * `$node.disconnected` to avoid stale routing after a worker crash.\n */\n private remoteNativeCaps: Map<number, Map<string, { addonId: string; nodeId: string }>> = new Map()\n\n /** Wait for a device-provider by addonId, returning null on timeout. */\n private async waitDeviceProvider(addonId: string, timeoutMs = 5_000): Promise<IDeviceProviderCap | null> {\n const provider = await this.capabilityRegistry?.waitForProvider('device-provider', addonId, timeoutMs)\n return provider ? (provider as IDeviceProviderCap) : null\n }\n\n /** Require a device-provider by addonId — throws if not found. */\n private async requireDeviceProvider(addonId: string): Promise<IDeviceProviderCap> {\n const dp = await this.waitDeviceProvider(addonId)\n if (!dp) {\n throw new Error(`Device provider \"${addonId}\" not found or not registered`)\n }\n return dp\n }\n\n // ── Binding store (persisted wrapper activation) ───────────────────────────\n //\n // These methods are public so the device-manager capability router can\n // expose them as cap methods. They read/write through\n // ctx.settings.readAddonStore/writeAddonStore — the same persistence channel\n // the rest of the addon uses.\n\n private async readBindingsStore(): Promise<DeviceBindingsStore> {\n const raw = (await this.ctx.settings!.readAddonStore()) as Partial<DeviceBindingsStore>\n return { deviceBindings: raw.deviceBindings ?? {} }\n }\n\n private async writeBindingsStore(next: DeviceBindingsStore): Promise<void> {\n await this.ctx.settings!.writeAddonStore({ deviceBindings: next.deviceBindings })\n }\n\n private resolveWrapperNodeId(_wrapperAddonId: string): string {\n // TODO(phase-7): read from cluster view so cluster-deployed wrappers\n // report their actual node. For now assume hub-co-located.\n return 'hub'\n }\n\n /**\n * Active discovery of native caps registered on a worker node.\n * Called from the `$node.connected` handler to recover from lost\n * `DeviceBindingsChanged` broadcasts after worker restart.\n *\n * Iterates the broker's service registry, picks\n * `<addonId>.native-provider.<capName>` services owned by the\n * connected node, calls each service's `$listDeviceIds` action to\n * fetch the deviceIds the worker has registered that cap on, then\n * writes entries into `remoteNativeCaps`.\n *\n * Best-effort: per-service failures are logged and swallowed so a\n * single bad service doesn't abort the whole rebuild.\n */\n private async discoverWorkerNativeCaps(connectedNodeId: string, _connectedAddonId: string): Promise<void> {\n const cluster = this.ctx.kernel.cluster\n if (!cluster) return\n const broker = cluster.broker as unknown as {\n registry?: {\n getServiceList?: (opts: Record<string, boolean>) => readonly { name: string; nodeID: string; metadata?: Record<string, unknown> }[]\n }\n call?: (action: string, params: unknown, opts?: { nodeID?: string }) => Promise<unknown>\n }\n const services = broker.registry?.getServiceList?.({ onlyAvailable: true, withActions: false }) ?? []\n const NATIVE_INFIX = '.native-provider.'\n // Filter on nodeID + the `.native-provider.` infix only. The\n // service-name addonId prefix may differ from the addonId we'd\n // derive from the nodeId (groupId suffixes like `-isolated`), so\n // we extract addonId + capName straight from the service name.\n const matched = services.filter((s) =>\n s.nodeID === connectedNodeId && s.name.includes(NATIVE_INFIX),\n )\n if (matched.length === 0) return\n for (const svc of matched) {\n const idx = svc.name.indexOf(NATIVE_INFIX)\n if (idx <= 0) continue\n const addonId = svc.name.slice(0, idx)\n const capName = svc.name.slice(idx + NATIVE_INFIX.length)\n if (!addonId || !capName) continue\n try {\n const action = `${svc.name}.$listDeviceIds`\n const deviceIds = await broker.call?.(action, {}, { nodeID: connectedNodeId }) as readonly number[] | undefined\n if (!deviceIds || deviceIds.length === 0) continue\n for (const deviceId of deviceIds) {\n if (this.capabilityRegistry?.getNativeAddonId(capName, deviceId)) continue\n let perDevice = this.remoteNativeCaps.get(deviceId)\n if (!perDevice) {\n perDevice = new Map()\n this.remoteNativeCaps.set(deviceId, perDevice)\n }\n perDevice.set(capName, { addonId, nodeId: connectedNodeId })\n }\n this.ctx.logger.debug('worker native-cap discovered', {\n meta: { nodeId: connectedNodeId, addonId, capName, deviceIds },\n })\n } catch (err) {\n this.ctx.logger.debug('worker native-cap $listDeviceIds failed', {\n meta: { service: svc.name, nodeId: connectedNodeId, error: errMsg(err) },\n })\n }\n }\n this.ctx.logger.info('worker native-cap discovery completed', {\n meta: { nodeId: connectedNodeId, services: matched.length },\n })\n }\n\n async getBindings(input: { deviceId: number }): Promise<{ deviceId: number; entries: DeviceBindingEntry[] }> {\n // Every map (bindings store, native-cap registry, remoteNativeCaps) is\n // keyed by numeric deviceId now that Phase 2 finished the migration.\n const storeKey = String(input.deviceId)\n const store = await this.readBindingsStore()\n const perDevice = store.deviceBindings[storeKey] ?? {}\n\n const entries: DeviceBindingEntry[] = []\n const seenCaps = new Set<string>()\n\n // 1. Entries from persisted wrapper activations\n for (const [capName, { wrapperAddonId }] of Object.entries(perDevice)) {\n const hubLocalNative = this.capabilityRegistry?.getNativeAddonId(capName, input.deviceId) ?? null\n const remoteNative = this.remoteNativeCaps.get(input.deviceId)?.get(capName) ?? null\n const nativeAddonId = hubLocalNative ?? remoteNative?.addonId ?? ''\n const nativeNodeId = hubLocalNative\n ? (this.ctx.kernel.localNodeId ?? 'hub')\n : (remoteNative?.nodeId ?? (this.ctx.kernel.localNodeId ?? 'hub'))\n // Operator explicitly disabled the wrapper AND no native is available\n // → cap is genuinely off for this device. Mark it seen so step 2's\n // defaultActive auto-bind doesn't silently re-bind the wrapper.\n if (wrapperAddonId === null && !nativeAddonId) {\n seenCaps.add(capName)\n continue\n }\n entries.push({\n capName,\n kind: wrapperAddonId ? 'wrapped' : 'native',\n providerAddonId: wrapperAddonId ?? nativeAddonId,\n providerNodeId: wrapperAddonId ? this.resolveWrapperNodeId(wrapperAddonId) : nativeNodeId,\n nativeAddonId,\n })\n seenCaps.add(capName)\n }\n\n const remote = this.remoteNativeCaps.get(input.deviceId)\n\n // 2. Auto-activated wrappers — any cap a wrapper addon declared with\n // `defaultActive: true` claims the binding HERE, ahead of native\n // discovery. The wrapper is the active provider; it delegates to\n // the native via `ctx.getNativeProvider`, so \"wrapper decorates\n // native\" is the intended runtime shape and the cap router resolves\n // locally on the wrapper's node instead of trying to forward to the\n // native's worker (cap routers like `snapshot` are mounted without\n // a `createRemoteProxy` because the wrapper handles cross-process\n // delegation internally).\n //\n // Step 1 (persisted activations) still wins: an operator-toggled\n // deactivation marks the cap seen with no entry, blocking this\n // branch from re-binding the wrapper.\n //\n // Pure-wrapper caps (motion-detection, audio-analysis,\n // detection-pipeline) have NO native — the wrapper is the only\n // implementation. They still bind here with an empty `nativeAddonId`\n // so consumers (UI bindings table, pipeline-orchestrator's\n // `isCapActiveForDevice` gate) see the wrapper as active by default.\n // Mirrors the fallback semantics of step 1 (`?? ''`).\n if (this.capabilityRegistry) {\n for (const capName of this.capabilityRegistry.getCapsWithDefaultWrapper()) {\n if (seenCaps.has(capName)) continue\n const defaultWrapperAddonId = this.capabilityRegistry.getDefaultWrapperForCap(capName)\n if (!defaultWrapperAddonId) continue\n const hubLocalNative = this.capabilityRegistry.getNativeAddonId(capName, input.deviceId) ?? null\n const remoteNative = remote?.get(capName) ?? null\n const nativeAddonId = hubLocalNative ?? remoteNative?.addonId ?? ''\n entries.push({\n capName,\n kind: 'wrapped',\n providerAddonId: defaultWrapperAddonId,\n providerNodeId: this.resolveWrapperNodeId(defaultWrapperAddonId),\n nativeAddonId,\n })\n seenCaps.add(capName)\n }\n }\n\n // 3. Entries from hub-local registered natives (no wrapper configured)\n if (this.capabilityRegistry) {\n for (const capName of this.capabilityRegistry.getNativeCapsForDevice(input.deviceId)) {\n if (seenCaps.has(capName)) continue\n const nativeAddonId = this.capabilityRegistry.getNativeAddonId(capName, input.deviceId) ?? ''\n entries.push({\n capName,\n kind: 'native',\n providerAddonId: nativeAddonId,\n providerNodeId: this.ctx.kernel.localNodeId ?? 'hub',\n nativeAddonId,\n })\n seenCaps.add(capName)\n }\n }\n\n // 4. Entries from remote (forked-worker) natives learned via\n // `device.bindings-changed` broadcasts. Union'd here so consumers see\n // every cap for the device regardless of owning process.\n if (remote) {\n for (const [capName, info] of remote) {\n if (seenCaps.has(capName)) continue\n entries.push({\n capName,\n kind: 'native',\n providerAddonId: info.addonId,\n providerNodeId: info.nodeId,\n nativeAddonId: info.addonId,\n })\n seenCaps.add(capName)\n }\n }\n\n return { deviceId: input.deviceId, entries }\n }\n\n /**\n * Whole-fleet binding dump. Iterates every device known to the\n * deviceRegistry and reuses the per-device `getBindings` resolver\n * for each — same routing rules, single round-trip. Used by\n * `SystemManager.init()` for warm-boot.\n *\n * Bindings change rarely (wrapper toggle, device add/remove) so\n * clients invalidate via the existing\n * `capability.binding-changed` event rather than re-fetching this\n * payload periodically.\n */\n async getAllBindings(): Promise<Array<{ deviceId: number; entries: DeviceBindingEntry[] }>> {\n const hubRegistry = this.ctx.kernel?.deviceRegistry\n if (!hubRegistry) return []\n const out: Array<{ deviceId: number; entries: DeviceBindingEntry[] }> = []\n for (const device of hubRegistry.getAll()) {\n out.push(await this.getBindings({ deviceId: device.id }))\n }\n return out\n }\n\n /**\n * Resolve a numeric deviceId to a stableId via persisted meta.\n * Used only by the device-identity section of the device-details\n * aggregator (see `buildBaseDeviceSection`) to surface the stableId as\n * a readonly display field. All runtime/registry lookups are keyed by\n * numeric deviceId; this helper is display-only.\n */\n private async lookupPersistedStableId(deviceId: number): Promise<string | undefined> {\n const store = (await this.ctx.settings!.readAddonStore()) as AddonStore\n const meta = store.deviceMeta ?? {}\n for (const [key, m] of Object.entries(meta)) {\n if (m.id === deviceId) {\n const sep = key.indexOf(':')\n if (sep < 0) continue\n return key.slice(sep + 1)\n }\n }\n return undefined\n }\n\n\n // ── Device-details aggregator ──────────────────────────────────────────────\n //\n // Walks the bindings for `deviceId` (native + wrapped), picks every\n // provider whose cap declares `exposesDeviceSettings: true`, and asks\n // each for its per-device contribution. Contributions are concatenated\n // into a single `ConfigUISchemaWithValues`; editable fields are tagged\n // with `writerCapName` + `writerAddonId` so `updateDeviceField` can\n // route the mutation back to the exact provider that produced them.\n //\n // `kind` selects settings vs live-info. The two aggregators share the\n // enumeration + tagging pipeline; only the method invoked on each\n // contributing provider differs.\n async getDeviceAggregate(\n deviceId: number,\n kind: 'settings' | 'live',\n ): Promise<ContributionShape | null> {\n const registry = this.capabilityRegistry\n if (!registry) {\n this.ctx.logger.debug('capability registry unavailable — aggregate empty', { meta: { kind } })\n return null\n }\n\n const method = kind === 'settings' ? 'getDeviceSettingsContribution' : 'getDeviceLiveContribution'\n\n // Enumerate EVERY capability with `exposesDeviceSettings: true`, not\n // just the caps for which this device has a per-device binding. The\n // system-wide singletons that hold per-device state (pipeline-\n // orchestrator assignments, motion-detection tuning, stream-broker\n // RTSP restream toggles, audio-analyzer VAD thresholds, …) are\n // registered once per system — they don't map to `(deviceId → cap)`\n // bindings. Walking bindings alone left their sections off the\n // aggregate, producing the \"5 empty fields\" device-details page.\n //\n // Each provider decides whether `deviceId` is relevant and returns\n // either a contribution or `null`. The aggregator flattens results\n // and tags each section with `(capName, addonId)` so the write path\n // can route a patch back to the provider that produced the field.\n const contributors: Array<{ capName: string; addonId: string }> = []\n for (const info of registry.listCapabilities()) {\n const def = registry.getDefinition(info.name)\n if (!def?.exposesDeviceSettings) continue\n // Deduplicate by base addon id — remote agents register with\n // \"addonId@nodeId\" keys but device settings are hub-centric:\n // only one contribution per logical addon, preferring the local\n // instance (shortest key = no \"@\" suffix).\n //\n // Skip synthetic addonIds (`<addonId>::native-<capName>`) created\n // for native-cap proxy services in `mountNativeCapService`. Those\n // services exist only to expose the cap's actual methods over\n // Moleculer; they don't implement contribution methods, so calling\n // `getDeviceSettingsContribution` on them throws and floods the\n // log. The owning addon (without the suffix) is the legitimate\n // contributor — it's listed separately and handled normally.\n const seen = new Set<string>()\n const sorted = [...info.providers].sort((a, b) => a.length - b.length)\n for (const addonId of sorted) {\n if (addonId.includes('::native-')) continue\n const baseId = addonId.includes('@') ? addonId.slice(0, addonId.indexOf('@')) : addonId\n if (seen.has(baseId)) continue\n seen.add(baseId)\n contributors.push({ capName: info.name, addonId })\n }\n }\n\n const results = await Promise.all(contributors.map(async ({ capName, addonId }) => {\n const provider = registry.getProviderByAddon<DeviceSettingsContribution>(capName, addonId)\n if (!provider) {\n // `listCapabilities()` returned this pair — it must resolve. A\n // null here means the registry's internal state diverged from\n // its public listing, which is always a bug (not a silent skip).\n throw new Error(\n `[device-manager] capability \"${capName}\" lists provider \"${addonId}\" but getProviderByAddon returned null — registry inconsistency`,\n )\n }\n // No typeof-function gate: `registerProvider` enforces that every\n // provider of an `exposesDeviceSettings` cap implements the three\n // contribution methods. Missing-method failures happen at register\n // time, not here.\n try {\n const contribution = await provider[method]({ deviceId })\n if (!contribution) return null\n return {\n capName,\n addonId,\n contribution: tagContribution(toWireShape(contribution), capName, addonId, kind),\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err)\n this.ctx.logger.warn('contribution method failed', { tags: { deviceId, addonId }, meta: { capName, method, error: msg } })\n return null\n }\n }))\n\n // Prepend the device-manager's own base section (only for settings —\n // live contribution from device-manager is currently empty). The base\n // section holds the device's identity + driver-specific config, tagged\n // with writer = 'device-manager' so updateDeviceField can route its\n // mutations back to `device.config.setAll`.\n const base = kind === 'settings' ? await this.buildBaseDeviceSection(deviceId) : null\n const parts: ContributionShape[] = [\n ...(base ? [tagContribution(base, 'device-manager', 'device-manager', kind)] : []),\n ...results.filter((r): r is { capName: string; addonId: string; contribution: ContributionShape } => r !== null).map(r => r.contribution),\n ]\n if (parts.length === 0) return null\n return mergeAggregates(parts)\n }\n\n /**\n * Build the device-manager's own contribution to the aggregator — the\n * device identity (id, stableId, addonId, type, online) + the\n * driver-specific config exposed by the device class via\n * `zodEntriesToConfigUI`.\n *\n * Two paths, deliberately symmetric with `getSettingsSchema`:\n *\n * - Hub-local: device's IDevice instance lives in this process'\n * DeviceRegistry, we read config + schema directly by reference.\n * - Cross-process: device lives in a forked worker (RtspCamera on\n * provider-rtsp, ONVIF on provider-onvif, …). We ask the worker's\n * `device-ops.getSettingsSchema` native provider for a wire-\n * serializable ConfigUISchema and merge it in under the same\n * \"Driver Config\" section, so the UI sees the same shape regardless\n * of where the IDevice physically runs.\n *\n * Returns `null` only when the device genuinely doesn't exist anywhere\n * (no hub-local, no persisted ownership, no device-ops native). The\n * aggregator falls back to contributor sections only in that case.\n */\n private async buildBaseDeviceSection(deviceId: number): Promise<ContributionShape | null> {\n const hubRegistry = this.ctx.kernel?.deviceRegistry\n const hubLocal = hubRegistry ? resolveDeviceById(hubRegistry, deviceId) : null\n const stableId = hubLocal?.device.stableId ?? await this.lookupPersistedStableId(deviceId)\n\n // Resolve identity fields regardless of where the device lives. The\n // capability-registry's native fallback populates remoteNativeCaps\n // from DeviceBindingsChanged events (so every worker-owned device\n // that registered device-ops shows up here with its addonId/nodeId).\n const nativeOwner = this.resolveNativeDeviceOwner(deviceId)\n const addonId = hubLocal?.addonId ?? nativeOwner?.addonId ?? null\n if (!hubLocal && !nativeOwner) return null\n\n const identityFields: unknown[] = [\n {\n type: 'text',\n key: '_deviceId',\n label: 'Device ID',\n readonlyField: true,\n value: String(deviceId),\n },\n {\n type: 'text',\n key: '_stableId',\n label: 'Stable ID',\n readonlyField: true,\n value: stableId ?? '',\n },\n {\n type: 'text',\n key: '_addonId',\n label: 'Driver',\n readonlyField: true,\n value: addonId ?? 'unknown',\n },\n ...(hubLocal\n ? [\n {\n type: 'text',\n key: '_type',\n label: 'Type',\n readonlyField: true,\n value: hubLocal.device.type,\n },\n {\n type: 'text',\n key: '_online',\n label: 'Online',\n readonlyField: true,\n value: hubLocal.device.online ? 'yes' : 'no',\n },\n ]\n : []),\n ]\n\n const sections: ContributionShape['sections'] = [\n {\n id: 'device-identity',\n title: 'Identity',\n tab: 'general',\n order: 0,\n fields: identityFields,\n },\n ]\n // Hardware-info metadata is exposed read-only via `getDevice` and\n // rendered by a dedicated UI tab — it's facts, not preferences,\n // so it doesn't belong in the editable settings aggregate.\n\n // Driver config — either built in-process from the live IDevice's Zod\n // schema (hub-local path) or obtained as a pre-built ConfigUISchema\n // from the worker that owns the device (cross-process path).\n const driverSchema = await this.resolveDriverConfigSchema(deviceId, hubLocal)\n if (driverSchema) {\n for (const section of driverSchema.sections) {\n sections.push({\n id: section.id,\n title: section.title,\n tab: section.tab ?? 'general',\n order: section.order ?? 1,\n fields: [...section.fields],\n ...(section.description !== undefined ? { description: section.description } : {}),\n ...(section.columns !== undefined ? { columns: section.columns } : {}),\n })\n }\n }\n\n // Propagate driver-declared tab metadata (label + icon + order).\n // Without this the merge step in `mergeAggregates` falls back to\n // auto-derived tabs (raw id as label, generic icon) — UI then shows\n // lowercase `sessions` / `streams` instead of the driver's\n // `Sessions` / `Streams`. First-wins inside mergeAggregates so the\n // driver's declarations beat any later contributor.\n return {\n sections,\n ...(driverSchema?.tabs ? { tabs: [...driverSchema.tabs] } : {}),\n }\n }\n\n /**\n * Lookup the native owner for `device-ops` on `deviceId` — the native-cap\n * registry (hub-local and remote) is keyed by numeric id.\n */\n private resolveNativeDeviceOwner(deviceId: number): { addonId: string; nodeId: string } | null {\n const local = this.capabilityRegistry?.getNativeAddonId('device-ops', deviceId) ?? null\n if (local) return { addonId: local, nodeId: this.ctx.kernel.localNodeId ?? 'hub' }\n const remote = this.remoteNativeCaps.get(deviceId)?.get('device-ops') ?? null\n return remote ? { addonId: remote.addonId, nodeId: remote.nodeId } : null\n }\n\n /**\n * Aggregate `status` across every registered cap for a device.\n *\n * Walks the supplied cap list (or `CAP_NAMES_WITH_STATUS` when\n * omitted), looks up a native provider per cap via the capability\n * registry, calls `provider.getStatus({ deviceId })`, and validates\n * the return against the cap's own `status.schema`. Validation\n * failures log a warning and yield `null` for that cap so the\n * overall aggregate stays usable — a single misbehaving provider\n * must not blank out a device's entire status view.\n *\n * Returned shape is `Record<capName, unknown | null>`; the client-\n * side hook tightens this to `CapStatusTypeMap` via the generated\n * `cap-status-types.ts`.\n */\n private async getDeviceStatusAggregate(input: {\n readonly deviceId: number\n readonly caps?: readonly string[]\n }): Promise<Record<string, unknown | null>> {\n const capNames: readonly string[] = input.caps ?? CAP_NAMES_WITH_STATUS\n const registry = this.capabilityRegistry\n const out: Record<string, unknown | null> = {}\n if (!registry) {\n for (const name of capNames) out[name] = null\n return out\n }\n\n await Promise.all(capNames.map(async (capName) => {\n try {\n const def = registry.getDefinition(capName)\n if (!def?.status) { out[capName] = null; return }\n\n const provider = registry.getNativeProvider<{\n getStatus?: (input: { deviceId: number }) => Promise<unknown>\n }>(capName, input.deviceId)\n if (!provider || typeof provider.getStatus !== 'function') {\n out[capName] = null\n return\n }\n\n const raw = await provider.getStatus({ deviceId: input.deviceId })\n if (raw == null) { out[capName] = null; return }\n\n // Validate against the cap's own schema — single misbehaving\n // provider must not poison the rest of the aggregate.\n const parsed = def.status.schema.safeParse(raw)\n if (!parsed.success) {\n this.ctx.logger.warn('getDeviceStatusAggregate: provider returned invalid status, dropping', {\n tags: { deviceId: input.deviceId },\n meta: { capName, issues: parsed.error.issues.slice(0, 3) },\n })\n out[capName] = null\n return\n }\n out[capName] = parsed.data\n } catch (err) {\n this.ctx.logger.warn('getDeviceStatusAggregate: provider threw, dropping', {\n tags: { deviceId: input.deviceId },\n meta: { capName, error: errMsg(err) },\n })\n out[capName] = null\n }\n }))\n\n return out\n }\n\n /**\n * Return the driver-specific device-settings contribution. Hub-local\n * devices call `getSettingsUISchema()` directly; forked-worker devices\n * go through the `device-ops.getSettingsSchema` cap method on the\n * numeric-id-keyed native registry.\n */\n private async resolveDriverConfigSchema(\n deviceId: number,\n hubLocal: { addonId: string; device: IDevice } | null,\n ): Promise<ContributionShape | null> {\n if (hubLocal) {\n const schema = hubLocal.device.getSettingsUISchema()\n return schema.sections.length === 0 ? null : toWireShape(schema)\n }\n const ops = this.capabilityRegistry?.getNativeProvider<IDeviceOpsProvider>('device-ops', deviceId)\n if (!ops) return null\n try {\n const schema = await ops.getSettingsSchema({ deviceId })\n if (!schema) return null\n const wire = schema as ConfigUISchemaWithValues\n return wire.sections.length === 0 ? null : toWireShape(wire)\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err)\n this.ctx.logger.warn('cross-process getSettingsSchema failed', { tags: { deviceId }, meta: { error: msg } })\n return null\n }\n }\n\n async updateDeviceField(input: {\n deviceId: number\n writerCapName: string\n writerAddonId: string\n key: string\n value: unknown\n }): Promise<{ success: true }> {\n // Special-case: the device-manager's own base section tags fields with\n // writer='device-manager'. Route those to `device.config.setAll` via the\n // live registry — the device-manager cap itself does not participate in\n // the binding-enumerated aggregator (it's a hub singleton), so it would\n // otherwise fail the capability lookup below.\n if (input.writerCapName === 'device-manager') {\n // Hub-local fast path — the device instance is in this process.\n const hubRegistry = this.ctx.kernel?.deviceRegistry\n const found = hubRegistry ? resolveDeviceById(hubRegistry, input.deviceId) : null\n if (found) {\n // Always go through `applySettingsPatch` — drivers whose UI shape\n // differs from storage (RtspCamera's flat mainStreamUrl projecting\n // onto a `streams: [{label,url}]` array) reshape here. `BaseDevice`\n // defaults the method to `config.setAll` so drivers that don't\n // reshape keep the old behaviour for free.\n await found.device.applySettingsPatch({ [input.key]: input.value })\n return { success: true as const }\n }\n // Cross-process fallback — device lives in a forked worker. Native\n // registry is numeric-keyed; dispatch through device-ops directly.\n const ops = this.capabilityRegistry?.getNativeProvider<IDeviceOpsProvider>('device-ops', input.deviceId)\n if (!ops) {\n throw new Error(`[device-manager] device \"${input.deviceId}\" not found (no hub-local entry, no device-ops native provider)`)\n }\n await ops.setConfig({ deviceId: input.deviceId, values: { [input.key]: input.value } })\n return { success: true as const }\n }\n\n const registry = this.capabilityRegistry\n if (!registry) {\n throw new Error('[device-manager] updateDeviceField requires capability registry — unavailable on this node')\n }\n\n const def = registry.getDefinition(input.writerCapName)\n if (!def?.exposesDeviceSettings) {\n throw new Error(`[device-manager] cap \"${input.writerCapName}\" does not expose device settings`)\n }\n\n const provider = registry.getProviderByAddon<DeviceSettingsContribution>(input.writerCapName, input.writerAddonId)\n if (!provider) {\n throw new Error(`[device-manager] provider \"${input.writerAddonId}\" not registered for cap \"${input.writerCapName}\"`)\n }\n\n await provider.applyDeviceSettingsPatch({\n deviceId: input.deviceId,\n patch: { [input.key]: input.value },\n })\n return { success: true as const }\n }\n\n /**\n * Batched counterpart of `updateDeviceField`. Groups changes by\n * `(writerCapName, writerAddonId)` so each contributor receives a\n * single `applyDeviceSettingsPatch` with all of its updates merged —\n * avoids N round-trips for simultaneous edits in the same save.\n *\n * Per-provider failures are captured in the `failures[]` output so the\n * admin UI can highlight which sections didn't persist; a failure on\n * one provider does NOT abort the others.\n */\n async updateDeviceFieldsBatch(input: {\n deviceId: number\n changes: ReadonlyArray<{\n writerCapName: string\n writerAddonId: string\n key: string\n value: unknown\n }>\n }): Promise<{ success: true; failures: { writerCapName: string; writerAddonId: string; error: string }[] }> {\n // Group by `(writerCapName, writerAddonId)` so we fire at most one\n // applyDeviceSettingsPatch per contributing provider.\n const groups = new Map<string, { writerCapName: string; writerAddonId: string; patch: Record<string, unknown> }>()\n for (const change of input.changes) {\n const key = `${change.writerCapName}::${change.writerAddonId}`\n const existing = groups.get(key)\n if (existing) {\n existing.patch[change.key] = change.value\n } else {\n groups.set(key, {\n writerCapName: change.writerCapName,\n writerAddonId: change.writerAddonId,\n patch: { [change.key]: change.value },\n })\n }\n }\n\n const failures: { writerCapName: string; writerAddonId: string; error: string }[] = []\n for (const group of groups.values()) {\n try {\n await this.applyGroupPatch(input.deviceId, group)\n } catch (err: unknown) {\n failures.push({\n writerCapName: group.writerCapName,\n writerAddonId: group.writerAddonId,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n return { success: true as const, failures }\n }\n\n /** Apply a single grouped patch to the appropriate provider. Mirrors\n * `updateDeviceField` routing (special-case device-manager, else\n * registry lookup). Used by `updateDeviceFieldsBatch`. */\n private async applyGroupPatch(\n deviceId: number,\n group: { writerCapName: string; writerAddonId: string; patch: Record<string, unknown> },\n ): Promise<void> {\n if (group.writerCapName === 'device-manager') {\n // Hub-local fast path (same reshape-aware path used by `updateDeviceField`).\n const hubRegistry = this.ctx.kernel?.deviceRegistry\n const found = hubRegistry ? resolveDeviceById(hubRegistry, deviceId) : null\n if (found) {\n await found.device.applySettingsPatch(group.patch)\n return\n }\n // Cross-process fallback via `device-ops.setConfig` — native registry\n // is numeric-keyed.\n const ops = this.capabilityRegistry?.getNativeProvider<IDeviceOpsProvider>('device-ops', deviceId)\n if (!ops) {\n throw new Error(`[device-manager] device \"${deviceId}\" not found (no hub-local entry, no device-ops native provider)`)\n }\n await ops.setConfig({ deviceId, values: group.patch })\n return\n }\n\n const registry = this.capabilityRegistry\n if (!registry) throw new Error('[device-manager] capability registry unavailable')\n const def = registry.getDefinition(group.writerCapName)\n if (!def?.exposesDeviceSettings) {\n throw new Error(`[device-manager] cap \"${group.writerCapName}\" does not expose device settings`)\n }\n const provider = registry.getProviderByAddon<DeviceSettingsContribution>(group.writerCapName, group.writerAddonId)\n if (!provider) {\n throw new Error(`[device-manager] provider \"${group.writerAddonId}\" not registered for cap \"${group.writerCapName}\"`)\n }\n await provider.applyDeviceSettingsPatch({ deviceId, patch: group.patch })\n }\n\n async listWrappersForCap(input: { capName: string }): Promise<string[]> {\n return [...(this.capabilityRegistry?.getWrappersForCap(input.capName) ?? [])]\n }\n\n async listBindableCapsForDeviceType(input: { deviceType: string }): Promise<Array<{ capName: string; wrappers: string[] }>> {\n const registry = this.capabilityRegistry\n if (!registry) return []\n const capNames = registry.listDeviceScopedCapsForType(input.deviceType)\n return capNames.map(capName => ({\n capName,\n wrappers: [...registry.getWrappersForCap(capName)],\n }))\n }\n\n async setWrapperActive(input: {\n deviceId: number\n capName: string\n wrapperAddonId: string\n active: boolean\n }): Promise<void> {\n const storeKey = String(input.deviceId)\n const store = await this.readBindingsStore()\n const perDevice = { ...(store.deviceBindings[storeKey] ?? {}) }\n if (input.active) {\n perDevice[input.capName] = { wrapperAddonId: input.wrapperAddonId }\n } else {\n // Explicit \"operator disabled\" marker. Persisting {wrapperAddonId:null}\n // (instead of deleting) prevents getBindings step 4 from re-binding\n // the defaultActive wrapper after a toggle-off. When a native provider\n // exists, the null marker still surfaces as `kind:'native'` (the revert\n // path the old delete branch was optimising for); when no native is\n // registered, the cap stays truly unbound for this device.\n perDevice[input.capName] = { wrapperAddonId: null }\n }\n const nextDeviceBindings = Object.keys(perDevice).length > 0\n ? { ...store.deviceBindings, [storeKey]: perDevice }\n : (() => { const { [storeKey]: _drop, ...rest } = store.deviceBindings; return rest })()\n await this.writeBindingsStore({ deviceBindings: nextDeviceBindings })\n this.ctx.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'addon', id: this.ctx.id },\n category: EventCategory.DeviceBindingsChanged,\n data: {\n deviceId: input.deviceId,\n capName: input.capName,\n reason: input.active ? 'wrapper-activated' : 'wrapper-deactivated',\n addonId: input.wrapperAddonId,\n nodeId: this.resolveWrapperNodeId(input.wrapperAddonId),\n },\n })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[] | void> {\n const settings = this.ctx.settings\n if (!settings) {\n this.ctx.logger.warn('ctx.settings not available — device persistence unavailable')\n return\n }\n\n const registry = this.ctx.kernel.deviceRegistry ?? null\n\n if (!registry) {\n this.ctx.logger.warn('device-registry not available — live operations will use persisted data only')\n }\n\n // Cross-process device-ops dispatch. Forked-worker addons (e.g.\n // provider-rtsp) register devices whose live IDevice instance lives in\n // the worker's own DeviceRegistry — invisible to this hub-side\n // `registry`. Instead of a bespoke Moleculer bridge, every worker\n // auto-registers a `device-ops` NATIVE capability provider per device\n // (see createBrokerDeviceManagerApi.register). Hub consumers reach it\n // via `capabilityRegistry.getNativeProvider('device-ops', deviceId)`:\n // the CapabilityRegistry's native-fallback builds a typed proxy that\n // routes the call to the owning worker through the standard native-cap\n // bridge — same plumbing every other per-device cap (snapshot, events,\n // ptz, …) already uses.\n\n // Cross-process native-cap discovery. Workers emit `device.bindings-changed`\n // on `registerNativeCap` / device removal; we mirror those into\n // `remoteNativeCaps` so `getBindings` returns the full cluster view.\n // Events from the local node are ignored: hub-local natives live in\n // `capabilityRegistry` and are folded in directly by getBindings.\n const localNodeId = this.ctx.kernel.localNodeId ?? 'hub'\n this.ctx.eventBus.subscribe(\n { category: EventCategory.DeviceBindingsChanged },\n (event) => {\n const { deviceId, capName, reason, addonId, nodeId } = event.data\n if (nodeId === localNodeId) return\n if (reason === 'native-registered') {\n let perDevice = this.remoteNativeCaps.get(deviceId)\n if (!perDevice) {\n perDevice = new Map()\n this.remoteNativeCaps.set(deviceId, perDevice)\n }\n perDevice.set(capName, { addonId, nodeId })\n } else if (reason === 'native-unregistered') {\n const perDevice = this.remoteNativeCaps.get(deviceId)\n if (!perDevice) return\n perDevice.delete(capName)\n if (perDevice.size === 0) this.remoteNativeCaps.delete(deviceId)\n }\n },\n )\n\n // Purge every remote native-cap entry owned by a node that just dropped.\n // The worker will re-emit `native-registered` on its next boot — no\n // persistence needed here.\n const cluster = this.ctx.kernel.cluster\n if (cluster) {\n cluster.broker.localBus.on('$node.disconnected', (payload) => {\n const gone = payload.node.id\n const emptyDevices: number[] = []\n for (const [deviceId, perDevice] of this.remoteNativeCaps) {\n const toDelete: string[] = []\n for (const [capName, entry] of perDevice) {\n if (entry.nodeId === gone) toDelete.push(capName)\n }\n for (const capName of toDelete) perDevice.delete(capName)\n if (perDevice.size === 0) emptyDevices.push(deviceId)\n }\n for (const deviceId of emptyDevices) this.remoteNativeCaps.delete(deviceId)\n })\n }\n // Resolve the per-device `device-ops` native provider. Works hub-local\n // (direct CapabilityRegistry lookup) and cross-process (the native-cap\n // fallback builds a proxy that routes to the owning worker). Consumers\n // that previously spoke the `<addonId>.device-ops.device.<method>`\n // Moleculer action go through this path instead — no bridge, no\n // parallel transport, just the standard native-cap dispatch.\n const requireDeviceOps = (deviceId: number): IDeviceOpsProvider => {\n const ops = this.capabilityRegistry?.getNativeProvider<IDeviceOpsProvider>('device-ops', deviceId)\n if (!ops) {\n throw new Error(`[device-manager] device-ops native provider not found for '${deviceId}'`)\n }\n return ops\n }\n\n // ── Helpers ──────────────────────────────────────────────────────────────\n\n const readStore = async (): Promise<AddonStore> => {\n return (await settings.readAddonStore()) as AddonStore\n }\n\n const readIndex = async (): Promise<Record<string, string[]>> => {\n const store = await readStore()\n return store.deviceIndex ?? {}\n }\n\n const readMeta = async (): Promise<Record<string, PersistedDeviceMeta>> => {\n const store = await readStore()\n return store.deviceMeta ?? {}\n }\n\n /** Hardware-identity metadata map. Lives in a sibling key on the\n * device-manager addon store so its writers (`setMetadata`) never\n * collide with the lifecycle writers on `deviceMeta`\n * (`registerDevice` / `setName` / `setLocation` / `setDisabled`).\n * Single-writer per row eliminates the \"writer X clobbers writer\n * Y's field\" bug class — `setMetadata` is the only producer. */\n const readMetadataMap = async (): Promise<Record<string, Record<string, unknown>>> => {\n const store = await readStore()\n return store.deviceMetadata ?? {}\n }\n\n // Serialize every read-modify-write of the deviceMeta / deviceIndex\n // blob through a single promise chain. Without this, concurrent\n // setMetadata / setName / setLocation / setDisabled callers race:\n // each reads the same `allMeta` snapshot, patches its own row,\n // then writes back the FULL blob — last writer wins, prior patches\n // are silently lost. The race fired on every addon restart when 5\n // Reolink cameras populate metadata in parallel and only the last\n // write survives. The mutex queues writes; reads in between still\n // hit `readStore()` directly (no penalty for hot read paths).\n let metaWriteChain: Promise<unknown> = Promise.resolve()\n const withMetaWriteLock = async <T>(fn: () => Promise<T>): Promise<T> => {\n const previous = metaWriteChain\n let release: () => void = () => {}\n const next = new Promise<void>((resolve) => { release = resolve })\n metaWriteChain = next\n try {\n await previous.catch(() => { /* swallow — keep chain alive */ })\n return await fn()\n } finally {\n release()\n }\n }\n\n /**\n * Resolve a numeric deviceId to the owning `(addonId, stableId)` pair.\n * Scans persisted meta — live IDevice lookup (hub registry) is handled\n * separately per call site so callers can decide whether to route to\n * an in-process driver or to the cross-process `device-ops` bridge.\n * Returns null when no device with that id is known to the hub.\n */\n const resolvePersistedById = async (\n deviceId: number,\n ): Promise<{ addonId: string; stableId: string; meta: PersistedDeviceMeta } | null> => {\n const meta = await readMeta()\n for (const [key, m] of Object.entries(meta)) {\n if (m.id === deviceId) {\n const sep = key.indexOf(':')\n if (sep < 0) continue\n return { addonId: key.slice(0, sep), stableId: key.slice(sep + 1), meta: m }\n }\n }\n return null\n }\n\n // ── Provider implementation ───────────────────────────────────────────────\n\n // Synchronous ownership cache, keyed by NUMERIC deviceId now that the\n // hub allocates ids before construction. The persisted meta store is\n // authoritative but reads are async; hub-side callers (e.g.\n // `CapabilityRegistry.getNativeProvider` fallback) need ownership\n // without awaiting. We keep this map in sync with every\n // registerDevice/removeDevice the provider serves, and warm it from\n // persistence on boot.\n const idToAddonId = new Map<number, string>()\n {\n const meta = await readMeta()\n for (const [key, m] of Object.entries(meta)) {\n const sep = key.indexOf(':')\n if (sep < 0) continue\n idToAddonId.set(m.id, key.slice(0, sep))\n }\n }\n\n // `$node.connected` bootstrap seed for cross-process `device-ops`. When a\n // forked worker reconnects after a hub restart, its `registerNativeCap`\n // broadcast for the auto-registered `device-ops` cap can be lost if the\n // Moleculer transport is still mid-handshake at the moment of emit — the\n // `broker.broadcast($cluster-events, …)` reaches no peer. `remoteNativeCaps`\n // then stays empty for that device forever, and every `getBindings` /\n // `requireDeviceOps` call on the hub throws \"device-ops native provider\n // not found\", which in turn blocks `stream-broker` from wiring the\n // camera and every tRPC `getStreamSources` from succeeding.\n //\n // Since `device-ops` is auto-registered for every device by contract\n // (see `createBrokerDeviceManagerApi.register`), ownership alone is\n // enough to synthesize a valid entry: if addon X owns deviceId N (per\n // persisted meta), then X's worker MUST host `device-ops` for N.\n // Routing through `buildNativeCapProxy` keys on `addonId` only —\n // Moleculer resolves the node — so the synthetic entry is always\n // correct once the worker's service is mounted.\n //\n // Idempotent: a late-arriving real `DeviceBindingsChanged` event\n // overwrites the entry with the authoritative payload. Hub-local\n // natives are skipped: those sit in `capabilityRegistry` and are\n // folded in directly by `getBindings` / `getNativeProvider`.\n if (cluster) {\n cluster.broker.localBus.on('$node.connected', (payload: { node: { id: string } }) => {\n const connectedNodeId = payload.node.id\n const lastSlash = connectedNodeId.lastIndexOf('/')\n if (lastSlash < 0) return\n const connectedAddonId = connectedNodeId.slice(lastSlash + 1)\n if (connectedAddonId.length === 0) return\n for (const [deviceId, ownerAddonId] of idToAddonId) {\n if (ownerAddonId !== connectedAddonId) continue\n if (this.capabilityRegistry?.getNativeAddonId('device-ops', deviceId)) continue\n let perDevice = this.remoteNativeCaps.get(deviceId)\n if (!perDevice) {\n perDevice = new Map()\n this.remoteNativeCaps.set(deviceId, perDevice)\n }\n if (!perDevice.has('device-ops')) {\n perDevice.set('device-ops', { addonId: connectedAddonId, nodeId: connectedNodeId })\n }\n }\n // Active discovery for ALL native caps the worker hosts. The\n // `device-ops` seed above only covers one cap because device\n // ownership trivially implies it; for other caps (intercom,\n // ptz, motion, osd, …) we need to ASK the worker which devices\n // it has registered each cap on. Without this poll, an\n // `addon-restart` of a forked worker leaves `remoteNativeCaps`\n // empty for every cap whose `DeviceBindingsChanged` broadcast\n // got lost in the transport handshake window — the symptom is\n // bindings UI dropping from 13→7 entries until full server\n // restart.\n //\n // The poll is deferred to the next tick so service discovery\n // has time to register the worker's `*.native-provider.*`\n // services. We then iterate the broker's service registry,\n // pick services that match this connected node, and call\n // `$listDeviceIds` on each (ports the worker-side\n // `workerNativeCaps` map). Per-device entries are written\n // straight into `remoteNativeCaps`; a late real\n // `DeviceBindingsChanged` event idempotently overwrites them.\n setTimeout(() => {\n this.discoverWorkerNativeCaps(connectedNodeId, connectedAddonId).catch((err) => {\n this.ctx.logger.warn('worker native-cap discovery failed', {\n meta: { nodeId: connectedNodeId, addonId: connectedAddonId, error: errMsg(err) },\n })\n })\n }, 500)\n })\n }\n\n const allocateNextDeviceId = async (): Promise<number> => {\n // Read-modify-write under the same settings writer the rest of the\n // addon uses — sqlite-settings serializes writes per addon, so two\n // concurrent registerDevice calls are ordered and each gets a unique\n // id. No in-memory counter: the store is the single source of truth.\n const store = await readStore()\n const current = store.nextDeviceId ?? 1\n await settings.writeAddonStore({ nextDeviceId: current + 1 })\n return current\n }\n\n const provider: IDeviceManagerProvider & {\n resolveDeviceOwnerSync: (deviceId: number) => string | null\n resolveNativeCapOwnerSync: (capName: string, deviceId: number) => { addonId: string; nodeId: string } | null\n } = {\n\n /** Sync ownership lookup backing persistence fallbacks (e.g. remove()\n * when the owning worker is offline). Ownership is keyed by numeric\n * deviceId → owning addonId as recorded in the persisted meta. NOT\n * a native-cap lookup: an addon can own a device without registering\n * every possible cap natively (e.g. RtspCamera without snapshotUrl\n * doesn't register the snapshot cap). Use\n * `resolveNativeCapOwnerSync` for cap-resolution paths.\n */\n resolveDeviceOwnerSync: (deviceId: number): string | null => {\n return idToAddonId.get(deviceId) ?? null\n },\n\n /** Sync lookup for the addon that registered a native provider for\n * `(capName, deviceId)`. Backs `CapabilityRegistry`'s native fallback\n * so the hub only synthesizes a cross-process proxy when the cap is\n * actually published — never on speculative device ownership.\n *\n * Consults hub-local registrations first (in-process natives),\n * then the `remoteNativeCaps` map populated from\n * `DeviceBindingsChanged` events emitted by forked-worker\n * `registerNativeCap` calls. Both are generic: any addon that hosts\n * devices and registers caps via the standard context API shows up\n * here without per-addon branching.\n */\n resolveNativeCapOwnerSync: (capName: string, deviceId: number): { addonId: string; nodeId: string } | null => {\n const localAddonId = this.capabilityRegistry?.getNativeAddonId(capName, deviceId) ?? null\n if (localAddonId) {\n return { addonId: localAddonId, nodeId: this.ctx.kernel.localNodeId ?? 'hub' }\n }\n const remote = this.remoteNativeCaps.get(deviceId)?.get(capName) ?? null\n if (remote) return { addonId: remote.addonId, nodeId: remote.nodeId }\n return null\n },\n\n // ── Persistence ────────────────────────────────────────────────────────\n\n /** Idempotent numeric-id reservation. Callers invoke this before\n * constructing the owning `IDevice` so `DeviceContext.id` is bound\n * at construction time. A repeat call for the same `(addonId,\n * stableId)` returns the already-persisted id — same physical\n * device reconnecting after a driver restart keeps its original\n * number. Fresh pairs burn one slot from the monotonic\n * `nextDeviceId` counter and seed a meta placeholder so the\n * `deviceMeta` → `id` invariant holds even before\n * `registerDevice` completes. */\n allocateDeviceId: async (input) => {\n const { addonId, stableId } = input\n const key = deviceKey(addonId, stableId)\n return await withMetaWriteLock(async () => {\n const meta = await readMeta()\n const existing = meta[key]\n if (existing) return { id: existing.id }\n const id = await allocateNextDeviceId()\n await settings.writeAddonStore({\n deviceMeta: {\n ...meta,\n [key]: {\n type: 'generic',\n name: stableId,\n location: null,\n disabled: false,\n parentDeviceId: null,\n id,\n },\n },\n })\n return { id }\n })\n },\n\n registerDevice: async (input) => {\n const { addonId, stableId, id, type, name, parentDeviceId, features, config } = input\n const key = deviceKey(addonId, stableId)\n\n const featuresArr = Array.isArray(features) ? [...features] : []\n // Index update + meta write together under the lock so a\n // concurrent registerDevice for a different (addonId, stableId)\n // can't read/clobber our deviceMeta snapshot.\n const { isFirstRegistration } = await withMetaWriteLock(async () => {\n const index = await readIndex()\n const existing = index[addonId] ?? []\n const wasInIndex = existing.includes(stableId)\n if (!wasInIndex) {\n await settings.writeAddonStore({\n deviceIndex: { ...index, [addonId]: [...existing, stableId] },\n })\n }\n\n // Persist the full meta record. The kernel's `create()`\n // shortcut writes operator-supplied `name` + `location`\n // BEFORE construction via `setName` / `setLocation` actions,\n // so by the time we land here the placeholder name is already\n // overwritten. Preserve every operator-edited field; the\n // `name` arg coming through this action is the device's\n // construction-time `device.name` which already reflects\n // whatever the meta row carries (`BaseDevice` reads from\n // `ctx.deviceMeta.name`).\n const meta = await readMeta()\n const existingMeta = meta[key]\n // First registration vs subsequent meta refresh (e.g. post-probe\n // features update). Drives whether we emit a `DeviceRegistered`\n // event below — listeners (ReolinkHub discovery refresh, etc)\n // must not re-fire on every meta update or downstream work\n // multiplies.\n const isFirst = !existingMeta || !wasInIndex\n await settings.writeAddonStore({\n deviceMeta: {\n ...meta,\n [key]: {\n type,\n // Prefer the persisted name (set by `setName` pre-construct)\n // over the construction-time fallback. Only when the meta\n // row literally still carries `name === stableId` (the\n // placeholder seeded by `allocateDeviceId`) do we fall\n // back to the action arg.\n name: existingMeta && existingMeta.name !== stableId ? existingMeta.name : name,\n location: existingMeta?.location ?? null,\n disabled: existingMeta?.disabled ?? false,\n parentDeviceId,\n id,\n features: featuresArr,\n },\n },\n })\n return { isFirstRegistration: isFirst }\n })\n\n // Config is pre-persisted by the kernel's `create()` shortcut\n // via `persistConfig` BEFORE construction. We still accept a\n // non-empty `config` payload here for back-compat with paths\n // that haven't migrated, and write it through — but the\n // canonical CREATE path passes `config: {}` and the persisted\n // row stays untouched.\n if (Object.keys(config).length > 0) {\n await settings.writeDeviceStore(id, config)\n }\n\n idToAddonId.set(id, addonId)\n\n // Emit `DeviceRegistered` ONLY on first registration. Subsequent\n // calls (driver re-publishing post-probe features, accessory\n // count update, …) are silent meta refreshes — listeners that\n // care about the device existing already saw the first event.\n // Skipping the duplicate prevents listeners like ReolinkHub from\n // burning a discovery-refresh roundtrip per re-register.\n if (isFirstRegistration) {\n this.ctx.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'device', id },\n category: EventCategory.DeviceRegistered,\n data: {\n deviceId: id,\n name: (name.length > 0 ? name : stableId),\n providerId: addonId,\n parentDeviceId: parentDeviceId ?? null,\n },\n })\n } else {\n this.ctx.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'device', id },\n category: EventCategory.DeviceMetaChanged,\n data: {\n deviceId: id,\n name: (name.length > 0 ? name : stableId),\n providerId: addonId,\n parentDeviceId: parentDeviceId ?? null,\n features: featuresArr,\n },\n })\n }\n },\n\n removeDevice: async (input) => {\n const { deviceId } = input\n // Resolve `(addonId, stableId)` from the meta store. The cap input\n // only carries the numeric id now — internal cleanup still needs\n // the composite key for index/meta storage rows (those are the\n // external-linkage rows we deliberately keep stableId in).\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) {\n // Removal of an unknown device is a no-op (idempotent) — the\n // caller may be racing teardown after a crash. No throw: the\n // hub-level `remove()` already error-checks first.\n return\n }\n const { addonId, stableId, meta: persistedMeta } = persisted\n const key = deviceKey(addonId, stableId)\n const deviceName = persistedMeta.name\n\n await withMetaWriteLock(async () => {\n // Update index + meta + hardware-metadata together under the\n // lock so we can't lose a concurrent register's freshly-persisted\n // row by reading a pre-write snapshot of `meta`.\n const index = await readIndex()\n const remaining = (index[addonId] ?? []).filter(sid => sid !== stableId)\n const updatedIndex = remaining.length > 0\n ? { ...index, [addonId]: remaining }\n : (() => { const { [addonId]: _removed, ...rest } = index; return rest })()\n await settings.writeAddonStore({ deviceIndex: updatedIndex })\n\n const meta = await readMeta()\n const { [key]: _removedMeta, ...restMeta } = meta\n await settings.writeAddonStore({ deviceMeta: restMeta })\n\n const map = await readMetadataMap()\n if (key in map) {\n const { [key]: _removedMetadata, ...restMap } = map\n await settings.writeAddonStore({ deviceMetadata: restMap })\n }\n })\n\n // Delete all per-device config rows so no orphaned settings remain after removal.\n await settings.clearDeviceStore(deviceId)\n\n // Drop any persisted wrapper activations tied to this device so\n // `getBindings({deviceId})` stops returning stale entries after remove.\n const bindingsStore = await this.readBindingsStore()\n const bindingKey = String(deviceId)\n if (bindingsStore.deviceBindings[bindingKey]) {\n const { [bindingKey]: _removedBindings, ...restBindings } = bindingsStore.deviceBindings\n await this.writeBindingsStore({ deviceBindings: restBindings })\n }\n\n // Drop every cross-process native-cap mirror entry for this device.\n // The worker's device-ops bridge removeDevice handler doesn't emit\n // `native-unregistered` events today; doing the purge here keeps the\n // hub view consistent regardless of which teardown path ran.\n this.remoteNativeCaps.delete(deviceId)\n // Hub-local natives live in the CapabilityRegistry — clear them too\n // so in-process devices don't linger in `getNativeCapsForDevice`.\n this.capabilityRegistry?.unregisterAllNativeForDevice(deviceId)\n\n idToAddonId.delete(deviceId)\n\n this.ctx.logger.info('removed device', { tags: { deviceId, deviceName: deviceName.length > 0 ? deviceName : stableId } })\n this.ctx.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'device', id: deviceId },\n category: EventCategory.DeviceUnregistered,\n data: {\n deviceId,\n providerId: addonId,\n // Carry parentDeviceId so listeners can filter on \"is\n // this MY child?\" without consulting an in-memory map\n // they had to keep in sync. By the time this event\n // fires the meta row is already gone — we read it from\n // the resolved snapshot above.\n parentDeviceId: persistedMeta.parentDeviceId ?? null,\n },\n })\n },\n\n persistConfig: async (input) => {\n const { deviceId, data } = input\n // Per-device config is keyed by numeric id end-to-end. We still\n // verify the device is known (resolves through meta) so a write\n // for a non-existent id throws instead of silently allocating a\n // stray store entry.\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) throw new Error(`[device-manager] persistConfig: unknown device id=${deviceId}`)\n await settings.writeDeviceStore(deviceId, data)\n },\n\n loadConfig: async (input) => {\n const { deviceId } = input\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) return {}\n return settings.readDeviceStore(deviceId)\n },\n\n /**\n * Load the operator-organisational meta surface for one device\n * (`name` / `location` / `disabled` / `type` / `parentDeviceId`\n * / `addonId` + `id` / `stableId`). Used by the kernel proxy's\n * device-context factory to populate `ctx.deviceMeta` before\n * the device class constructor runs. Returns `null` when no\n * persisted row exists for the id.\n *\n * Reads default `location` to `null` and `disabled` to `false`\n * for legacy rows that predate the field — production code\n * relies on the IDevice type contract that both are present.\n */\n loadMeta: async (input) => {\n const { deviceId } = input\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) return null\n const { addonId, stableId, meta: m } = persisted\n const key = deviceKey(addonId, stableId)\n const map = await readMetadataMap()\n const metadata = map[key] ?? null\n return {\n id: m.id,\n stableId,\n addonId,\n type: m.type as DeviceType,\n name: m.name,\n location: m.location ?? null,\n disabled: m.disabled ?? false,\n parentDeviceId: m.parentDeviceId,\n metadata,\n }\n },\n\n /**\n * Update the operator-edited display name. Writes the meta\n * row, emits a `DeviceMetaChanged` event so live consumers\n * (UI device list, alert center) see the rename without\n * polling. The live `IDevice.name` mirror is updated by the\n * kernel proxy on its side (`device-cap-proxy.ts`).\n */\n setName: async (input) => {\n const { deviceId, name } = input\n await withMetaWriteLock(async () => {\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) throw new Error(`[device-manager] setName: unknown device id=${deviceId}`)\n const { addonId, stableId, meta: m } = persisted\n const key = deviceKey(addonId, stableId)\n const allMeta = await readMeta()\n await settings.writeAddonStore({\n deviceMeta: { ...allMeta, [key]: { ...m, name } },\n })\n })\n this.ctx.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'device', id: deviceId },\n category: EventCategory.DeviceMetaChanged,\n data: { deviceId, field: 'name', value: name },\n })\n },\n\n /**\n * Update the operator-organisational location label. `null`\n * clears it. Mirrors the same persist-then-emit shape as\n * `setName`; consumers subscribe to `DeviceMetaChanged` and\n * filter on `field: 'location'`.\n */\n setLocation: async (input) => {\n const { deviceId, location } = input\n await withMetaWriteLock(async () => {\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) throw new Error(`[device-manager] setLocation: unknown device id=${deviceId}`)\n const { addonId, stableId, meta: m } = persisted\n const key = deviceKey(addonId, stableId)\n const allMeta = await readMeta()\n await settings.writeAddonStore({\n deviceMeta: { ...allMeta, [key]: { ...m, location } },\n })\n })\n this.ctx.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'device', id: deviceId },\n category: EventCategory.DeviceMetaChanged,\n data: { deviceId, field: 'location', value: location },\n })\n },\n\n /**\n * Patch the device's hardware-identity metadata blob. Shallow\n * merge — `null` removes a key, anything else overwrites.\n * Drivers populate factual fields on first probe; operators\n * augment via the Device Info tab. Idempotent: a no-op patch\n * (every key already present with the same value) doesn't emit\n * the meta-changed event.\n */\n setMetadata: async (input) => {\n const { deviceId, patch } = input\n const result = await withMetaWriteLock(async () => {\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) throw new Error(`[device-manager] setMetadata: unknown device id=${deviceId}`)\n const { addonId, stableId } = persisted\n const key = deviceKey(addonId, stableId)\n const map = await readMetadataMap()\n const current = (map[key] ?? {}) as Record<string, unknown>\n const next: Record<string, unknown> = { ...current }\n let changed = false\n for (const [k, v] of Object.entries(patch)) {\n if (v === null) {\n if (k in next) {\n delete next[k]\n changed = true\n }\n } else if (next[k] !== v) {\n next[k] = v\n changed = true\n }\n }\n if (!changed) return { changed: false as const }\n const hasFields = Object.keys(next).length > 0\n const updatedMap = { ...map }\n if (hasFields) {\n updatedMap[key] = next\n } else {\n delete updatedMap[key]\n }\n await settings.writeAddonStore({ deviceMetadata: updatedMap })\n return { changed: true as const, finalMeta: hasFields ? next : null }\n })\n if (!result.changed) return\n this.ctx.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'device', id: deviceId },\n category: EventCategory.DeviceMetaChanged,\n data: { deviceId, field: 'metadata', value: result.finalMeta },\n })\n },\n\n /**\n * Soft-disable the device. Persisted on the meta row;\n * lifecycle gating is the driver's responsibility (BaseDevice\n * exposes `this.disabled` for the driver to consult at the top\n * of its lifecycle methods).\n */\n setDisabled: async (input) => {\n const { deviceId, disabled } = input\n await withMetaWriteLock(async () => {\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) throw new Error(`[device-manager] setDisabled: unknown device id=${deviceId}`)\n const { addonId, stableId, meta: m } = persisted\n const key = deviceKey(addonId, stableId)\n const allMeta = await readMeta()\n await settings.writeAddonStore({\n deviceMeta: { ...allMeta, [key]: { ...m, disabled } },\n })\n })\n this.ctx.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'device', id: deviceId },\n category: EventCategory.DeviceMetaChanged,\n data: { deviceId, field: 'disabled', value: disabled },\n })\n },\n\n loadRuntimeState: async (input) => {\n const { deviceId } = input\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) return {}\n const data = await settings.readDeviceRuntimeState(deviceId)\n // Seed the mirror at first load so the hub knows about every\n // existing slice immediately — providers might never call\n // `setCapSlice` if their runtime state doesn't change, and\n // the mirror would otherwise stay empty until the first\n // change.\n this.seedMirror(deviceId, data)\n return data\n },\n\n /**\n * Union of (1) operator-curated location registry and (2) labels\n * currently in use on persisted devices. Case-insensitive\n * dedupe (preserves the first-seen casing). Sorted\n * case-insensitively for stable UI. Drives the Device Info\n * location autocomplete.\n */\n listLocations: async () => {\n const store = (await settings.readAddonStore()) as AddonStore\n const meta = store.deviceMeta ?? {}\n const registry = store.locations ?? []\n // Map from lower-cased key to the first-seen casing so duplicate\n // entries with different cases collapse to one.\n const seen = new Map<string, string>()\n const consider = (raw: unknown): void => {\n if (typeof raw !== 'string') return\n const trimmed = raw.trim()\n if (trimmed.length === 0) return\n const key = trimmed.toLowerCase()\n if (!seen.has(key)) seen.set(key, trimmed)\n }\n for (const label of registry) consider(label)\n for (const m of Object.values(meta)) consider(m.location)\n return [...seen.values()].sort((a, b) =>\n a.localeCompare(b, undefined, { sensitivity: 'base' }),\n )\n },\n\n /**\n * Add a label to the curated location registry. Idempotent:\n * existing entries (case-insensitive match) are silently kept.\n * Empty / whitespace-only inputs throw — operators must supply a\n * meaningful label.\n */\n addLocation: async (input) => {\n const trimmed = input.name.trim()\n if (trimmed.length === 0) {\n throw new Error('[device-manager] addLocation: name must be non-empty')\n }\n const store = (await settings.readAddonStore()) as AddonStore\n const current = store.locations ?? []\n if (current.some((l) => l.toLowerCase() === trimmed.toLowerCase())) {\n // Already present — no-op (preserves first-seen casing).\n return\n }\n await settings.writeAddonStore({ locations: [...current, trimmed] })\n },\n\n /**\n * Remove a label from the curated registry. Match is\n * case-insensitive. Devices that still reference this label keep\n * their `meta.location` value (the registry is a suggestion\n * list, not a foreign key) — pass `cascade: true` to also clear\n * `setLocation` on every device that referenced this exact\n * label. Cascade only matches case-insensitively + trimmed, same\n * as the registry equality check.\n */\n removeLocation: async (input) => {\n const trimmed = input.name.trim()\n if (trimmed.length === 0) return\n const store = (await settings.readAddonStore()) as AddonStore\n const current = store.locations ?? []\n const remaining = current.filter((l) => l.toLowerCase() !== trimmed.toLowerCase())\n if (remaining.length !== current.length) {\n await settings.writeAddonStore({ locations: remaining })\n }\n if (input.cascade !== true) return\n\n const meta = store.deviceMeta ?? {}\n const updates: Record<string, PersistedDeviceMeta> = { ...meta }\n const cleared: number[] = []\n for (const [key, m] of Object.entries(meta)) {\n if (typeof m.location !== 'string') continue\n if (m.location.trim().toLowerCase() !== trimmed.toLowerCase()) continue\n updates[key] = { ...m, location: null }\n cleared.push(m.id)\n }\n if (cleared.length === 0) return\n await settings.writeAddonStore({ deviceMeta: updates })\n for (const deviceId of cleared) {\n this.ctx.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'device', id: deviceId },\n category: EventCategory.DeviceMetaChanged,\n data: { deviceId, field: 'location', value: null },\n })\n }\n },\n\n listPersistedByAddon: async (input) => {\n const { addonId } = input\n const [index, meta] = await Promise.all([readIndex(), readMeta()])\n const stableIds = index[addonId] ?? []\n return stableIds.map(stableId => {\n const m = meta[deviceKey(addonId, stableId)]!\n return {\n id: m.id,\n stableId,\n type: m.type,\n name: m.name,\n location: m.location ?? null,\n disabled: m.disabled ?? false,\n parentDeviceId: m.parentDeviceId,\n }\n })\n },\n\n // ── Live queries — serve from live registry when available ────────────\n\n listAll: async (input) => {\n const { addonId } = input\n\n // Union of two sources, de-duped on (addonId, stableId):\n // 1. Hub-process live registry: IDevice instances owned by this\n // process (hub-resident addons). Provides live `online`,\n // features, and isCamera.\n // 2. Persisted index (settings): authoritative cross-process ledger\n // including devices owned by forked workers, whose IDevice\n // instance lives in the worker's registry and is invisible to\n // the hub-registry.\n //\n // Live wins on the overlap, so hub-owned devices keep their accurate\n // online/features. Forked-worker devices are reconstructed from\n // persisted metadata + persisted config.\n const results: ReturnType<typeof toDeviceInfo>[] = []\n const seen = new Set<string>()\n\n const meta = await readMeta()\n // Hardware metadata lives in its own row; pre-load once and inject\n // into every result regardless of branch.\n const metadataMap = await readMetadataMap()\n\n if (registry) {\n const liveEntries = addonId\n ? registry.getAllForAddon(addonId).map(device => ({ addonId: addonId!, device }))\n : registry.getAllWithAddonId()\n for (const { addonId: aid, device } of liveEntries) {\n const key = deviceKey(aid, device.stableId)\n const metadata = metadataMap[key] ?? null\n const metaRow = meta[key] ?? null\n results.push(toDeviceInfo(aid, device, metadata, metaRow))\n seen.add(key)\n }\n }\n\n const index = await readIndex()\n const targetAddons = addonId ? [addonId] : Object.keys(index)\n for (const aid of targetAddons) {\n for (const stableId of (index[aid] ?? [])) {\n const key = deviceKey(aid, stableId)\n if (seen.has(key)) continue\n const m = meta[key]!\n const persistedType = m.type as DeviceType\n const persistedConfig = (await settings.readDeviceStore(m.id)) as Record<string, unknown>\n const metadata = metadataMap[key] ?? null\n results.push({\n id: m.id,\n stableId,\n addonId: aid,\n type: persistedType,\n name: m?.name ?? stableId,\n location: m?.location ?? null,\n disabled: m?.disabled ?? false,\n parentDeviceId: m?.parentDeviceId ?? null,\n // role is not persisted to disk — only set on the live\n // IDevice (`BaseDevice.role`) by accessory subclasses. The\n // forked-worker fallback path can't read it, so emit null;\n // accessories typically run in the same process as their\n // parent provider, so the live path covers them in practice.\n role: null,\n // registry===null was the legacy \"offline view\" signal; keep it\n // for that case so existing callers see online=false. When a\n // registry is present and the device lives elsewhere (forked\n // worker), default to online=true — persisted state implies it\n // was successfully registered by its owning process.\n online: registry !== null,\n features: persistedFeatures(m?.features),\n // Live IDevice isn't reachable from the hub, so infer\n // isCamera from the persisted DeviceType rather than the\n // method-presence check `toDeviceInfo` uses. Without this\n // forked-worker cameras looked non-camera to the stream-broker\n // and never got their RTSP restream wired up.\n isCamera: persistedType === DeviceType.Camera,\n config: persistedConfig ?? {},\n metadata,\n })\n }\n }\n return results\n },\n\n getDevice: async (input) => {\n const { deviceId } = input\n\n // Prefer live (hub-registry) when present; otherwise fall back to\n // persisted metadata so we also find devices owned by forked workers.\n if (registry) {\n const found = resolveDeviceById(registry, deviceId)\n if (found) {\n const key = deviceKey(found.addonId, found.device.stableId)\n const [map, metaMap] = await Promise.all([readMetadataMap(), readMeta()])\n const metadata = map[key] ?? null\n const metaRow = metaMap[key] ?? null\n return toDeviceInfo(found.addonId, found.device, metadata, metaRow)\n }\n }\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) return null\n const { addonId: aid, stableId, meta: m } = persisted\n const persistedConfig = (await settings.readDeviceStore(m.id)) as Record<string, unknown>\n const key = deviceKey(aid, stableId)\n const map = await readMetadataMap()\n const metadata = map[key] ?? null\n // Name now lives exclusively on the meta surface (mutated via\n // `setName` action) — the legacy config.name fallback is gone\n // since drivers no longer carry `name` in their config schema.\n return {\n id: deviceId,\n stableId,\n addonId: aid,\n type: m.type as DeviceType,\n name: m.name,\n location: m.location ?? null,\n disabled: m.disabled ?? false,\n parentDeviceId: m.parentDeviceId,\n role: null,\n online: true,\n features: persistedFeatures(m.features),\n isCamera: false,\n config: persistedConfig ?? {},\n metadata,\n }\n },\n\n getChildren: async (input) => {\n const { parentDeviceId } = input\n\n // Resolve parent ownership so we can scope the persisted-child scan\n // per owning addon. Children are always registered under the same\n // addonId as their parent. The hub registry's getChildren() is a\n // pure numeric filter (`d.parentDeviceId === parentDeviceId`) so\n // we no longer need the parent's stableId for live lookups.\n let ownerAddonId: string | null = null\n if (registry) {\n const liveParent = registry.getById(parentDeviceId)\n if (liveParent) {\n ownerAddonId = registry.getAddonId(parentDeviceId)\n }\n }\n if (!ownerAddonId) {\n const persisted = await resolvePersistedById(parentDeviceId)\n if (!persisted) return []\n ownerAddonId = persisted.addonId\n }\n\n // Same union pattern as listAll: live (hub-registry) wins, persisted\n // fills in devices owned by forked workers.\n const results: ReturnType<typeof toDeviceInfo>[] = []\n const seen = new Set<string>()\n\n const [index, meta, metadataMap] = await Promise.all([readIndex(), readMeta(), readMetadataMap()])\n\n if (registry) {\n const liveChildren = registry.getChildren(parentDeviceId)\n for (const device of liveChildren) {\n const key = deviceKey(ownerAddonId, device.stableId)\n const metadata = metadataMap[key] ?? null\n const metaRow = meta[key] ?? null\n results.push(toDeviceInfo(ownerAddonId, device, metadata, metaRow))\n seen.add(key)\n }\n }\n\n const stableIds = index[ownerAddonId] ?? []\n const persistedChildren = stableIds.filter(\n sid => meta[deviceKey(ownerAddonId!, sid)]?.parentDeviceId === parentDeviceId,\n )\n for (const childStableId of persistedChildren) {\n const key = deviceKey(ownerAddonId, childStableId)\n if (seen.has(key)) continue\n const m = meta[key]!\n const persistedConfig = (await settings.readDeviceStore(m.id)) as Record<string, unknown>\n const metadata = metadataMap[key] ?? null\n results.push({\n id: m.id,\n stableId: childStableId,\n addonId: ownerAddonId,\n type: m.type as DeviceType,\n name: m.name,\n location: m.location ?? null,\n disabled: m.disabled ?? false,\n parentDeviceId,\n role: null,\n online: registry !== null,\n features: persistedFeatures(m.features),\n isCamera: false,\n config: persistedConfig ?? {},\n metadata,\n })\n }\n return results\n },\n\n // ── Live runtime methods ──────────────────────────────────────────────\n\n getStreamSources: async (input) => {\n const { deviceId } = input\n // Hub-local path\n if (registry) {\n const found = resolveDeviceById(registry, deviceId)\n if (found) {\n // Non-camera devices have no streams by definition — return\n // an empty list immediately instead of round-tripping through\n // `device-ops`. Cross-process dispatch is strictly for\n // worker-owned devices, not for wrong-type hub-local ones.\n if (!isCameraDevice(found.device)) return []\n const sources: readonly StreamSourceEntry[] = await found.device.getStreamSources()\n return sources.map(s => ({\n id: s.id,\n label: s.label,\n protocol: s.protocol,\n url: s.url,\n resolution: s.resolution,\n fps: s.fps,\n bitrate: s.bitrate,\n codec: s.codec,\n profileHint: s.profileHint,\n }))\n }\n }\n // Cross-process fallback — dispatch through the `device-ops` native\n // provider, now keyed on the numeric id in both hub and worker.\n // Errors propagate: a transient worker error must NOT masquerade as\n // \"device has no streams\" — callers need the real error.\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) throw new Error(`[device-manager] Device with id ${deviceId} not found`)\n const ops = requireDeviceOps(deviceId)\n const sources = await ops.getStreamSources({ deviceId })\n return sources.map(s => ({ ...s }))\n },\n\n getConfigSchema: async (input) => {\n const { deviceId } = input\n if (registry) {\n const found = resolveDeviceById(registry, deviceId)\n if (found) {\n return found.device.config.entries().map(entry => ({\n key: entry.key,\n value: entry.value,\n ...(entry.description !== undefined ? { description: entry.description } : {}),\n }))\n }\n }\n // Cross-process fallback. Propagates the real error when the\n // owning worker is offline or the device genuinely doesn't exist —\n // surfacing \"no config\" silently used to let the UI render empty\n // forms for broken devices.\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) throw new Error(`[device-manager] Device with id ${deviceId} not found`)\n const ops = requireDeviceOps(deviceId)\n const entries = await ops.getConfigEntries({ deviceId })\n return entries.map(e => ({ ...e }))\n },\n\n getSettingsSchema: async (input) => {\n const { deviceId } = input\n // Single source of truth: every IDevice implements\n // `getSettingsUISchema`. Hub-local devices answer in-process;\n // forked-worker devices go through the `device-ops.getSettingsSchema`\n // native provider which delegates to the same method worker-side.\n if (registry) {\n const found = resolveDeviceById(registry, deviceId)\n if (found) {\n return found.device.getSettingsUISchema()\n }\n }\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) return null\n const ops = requireDeviceOps(deviceId)\n const schema = await ops.getSettingsSchema({ deviceId })\n return (schema as ConfigUISchemaWithValues | null) ?? null\n },\n\n updateConfig: async (input) => {\n const { deviceId } = input\n if (registry) {\n const found = resolveDeviceById(registry, deviceId)\n if (found) {\n await found.device.config.setAll(input.values)\n return { success: true as const }\n }\n }\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) throw new Error(`[device-manager] Device with id ${deviceId} not found`)\n const ops = requireDeviceOps(deviceId)\n await ops.setConfig({ deviceId, values: input.values })\n return { success: true as const }\n },\n\n // `enable` / `disable` write to deviceMeta.disabled (the canonical\n // soft-disable flag that drivers consult via `BaseDevice.disabled`\n // at lifecycle entrypoints, and that `toDeviceInfo` projects onto\n // the wire by reading the LATEST persisted meta row — see the\n // `metaRow` argument plumbed through `listAll` / `getDevice` /\n // `getChildren`).\n //\n // Earlier these wrappers wrote `enabled: true/false` into the\n // device CONFIG blob — wrong store entirely. The config write\n // succeeded but the flag the rest of the system reads\n // (deviceMeta) stayed unchanged, so the UI's enable/disable\n // toggle appeared to do nothing. Delegating to `setDisabled`\n // routes through the correct withMetaWriteLock path and emits\n // the `DeviceMetaChanged` event for live invalidation.\n //\n // NB: `BaseDevice.disabled` is `readonly` and bound at\n // construction (base-device.ts:142). Drivers that gate behaviour\n // off `this.disabled` only see the value the device was\n // instantiated with — flipping disabled at runtime updates the\n // wire response immediately, but lifecycle gating inside the\n // driver only takes effect on the next instantiation. That's\n // acceptable today: the operator-facing semantic is \"hide /\n // mark inactive in the UI\"; the heavyweight teardown semantic\n // is `remove`.\n enable: async (input) => {\n await provider.setDisabled({ deviceId: input.deviceId, disabled: false })\n return { success: true as const }\n },\n\n disable: async (input) => {\n await provider.setDisabled({ deviceId: input.deviceId, disabled: true })\n return { success: true as const }\n },\n\n remove: async (input) => {\n const { deviceId } = input\n // (a) Device lives in the hub registry → local teardown, then persistence.\n if (registry) {\n const live = resolveDeviceById(registry, deviceId)\n if (live) {\n const deviceName = live.device.name\n await live.device.removeDevice()\n registry.remove(deviceId)\n await provider.removeDevice({ deviceId })\n this.ctx.logger.info('removed hub-local device', { tags: { deviceId, deviceName } })\n return { success: true as const }\n }\n }\n // (b) Device is owned by a forked worker → invoke its `device-ops`\n // native provider to let the driver release resources, then\n // clear hub persistence.\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) throw new Error(`[device-manager] Device with id ${deviceId} not found`)\n const { meta: persistedMeta } = persisted\n try {\n const ops = requireDeviceOps(deviceId)\n await ops.removeDevice({ deviceId })\n } catch (err) {\n // Worker might be offline or missing — still clear persistence so\n // the UI recovers. The warning is best-effort diagnostics.\n this.ctx.logger.warn('remove via device-ops failed — clearing persistence anyway', { tags: { deviceId, deviceName: persistedMeta.name }, meta: { error: errMsg(err) } })\n }\n await provider.removeDevice({ deviceId })\n return { success: true as const }\n },\n\n getStreamProfileMap: async (input) => {\n if (!registry) return {}\n const found = resolveDeviceById(registry, input.deviceId)\n if (!found) return {}\n\n // Check for explicitly stored profile map first\n const storedMap: unknown = found.device.config.entries().find(e => e.key === '_profileMap')?.value\n if (storedMap !== undefined && typeof storedMap === 'object' && storedMap !== null) {\n return storedMap as Record<string, string>\n }\n\n // Derive from profileHint fields on stream sources\n if (!isCameraDevice(found.device)) return {}\n const sources: readonly StreamSourceEntry[] = await found.device.getStreamSources()\n const profileMap: Record<string, string> = {}\n for (const s of sources) {\n if (s.profileHint && s.id) {\n profileMap[s.profileHint] = s.id\n }\n }\n return profileMap\n },\n\n setStreamProfileMap: async (input) => {\n const { deviceId } = input\n if (registry) {\n const found = resolveDeviceById(registry, deviceId)\n if (found) {\n await found.device.config.setAll({ _profileMap: input.profileMap })\n return { success: true as const }\n }\n }\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) throw new Error(`[device-manager] Device with id ${deviceId} not found`)\n const ops = requireDeviceOps(deviceId)\n await ops.setConfig({ deviceId, values: { _profileMap: input.profileMap } })\n return { success: true as const }\n },\n\n probeStreams: async (input) => {\n const streamProbe = this.ctx.kernel.streamProbe\n if (!streamProbe) return []\n\n const sources = await provider.getStreamSources({ deviceId: input.deviceId })\n const results: Array<{\n streamId: string\n width?: number\n height?: number\n codec?: string\n fps?: number\n bitrateKbps?: number\n }> = []\n\n for (const s of sources) {\n if (!s.url) continue\n try {\n const metadata = await streamProbe.probe(s.url, { force: true })\n results.push({\n streamId: s.id,\n width: metadata.width,\n height: metadata.height,\n codec: metadata.codec,\n fps: metadata.fps,\n bitrateKbps: metadata.bitrateKbps,\n })\n } catch (err) {\n this.ctx.logger.debug('streamProbe.probe failed — returning placeholder', {\n meta: {\n deviceId: input.deviceId,\n streamId: s.id,\n error: err instanceof Error ? err.message : String(err),\n },\n })\n results.push({ streamId: s.id })\n }\n }\n\n return results\n },\n\n // ── Device-provider operations (routed via CapabilityRegistry) ───────\n\n discoverDevices: async (input) => {\n const dp = await this.requireDeviceProvider(input.addonId)\n\n const supported = await dp.supportsDiscovery({})\n if (!supported) {\n throw new Error(`Addon \"${input.addonId}\" does not support device discovery`)\n }\n\n const discovered = await dp.discoverDevices({})\n return discovered.map((d) => ({\n stableId: d.stableId,\n type: d.type,\n suggestedName: d.suggestedName,\n prefilledConfig: d.prefilledConfig,\n }))\n },\n\n adoptDevice: async (input) => {\n const dp = await this.requireDeviceProvider(input.addonId)\n\n const supported = await dp.supportsDiscovery({})\n if (!supported) {\n throw new Error(`Addon \"${input.addonId}\" does not support device adoption`)\n }\n\n return dp.adoptDiscoveredDevice({\n candidate: input.candidate as Parameters<IDeviceProviderCap['adoptDiscoveredDevice']>[0]['candidate'],\n })\n },\n\n getCreationSchema: async (input) => {\n const dp = await this.requireDeviceProvider(input.addonId)\n\n const supported = await dp.supportsManualCreation({})\n if (!supported) return null\n\n const schema = await dp.getChildCreationSchema({ type: input.type })\n return schema ?? null\n },\n\n createDevice: async (input) => {\n const dp = await this.requireDeviceProvider(input.addonId)\n\n const supported = await dp.supportsManualCreation({})\n if (!supported) {\n throw new Error(`Addon \"${input.addonId}\" does not support manual device creation`)\n }\n\n return dp.createDevice({ type: input.type, config: input.config })\n },\n\n testCreationField: async (input) => {\n const dp = await this.requireDeviceProvider(input.addonId)\n return dp.testCreationField({\n type: input.type,\n key: input.key,\n value: input.value,\n ...(input.formValues !== undefined ? { formValues: input.formValues } : {}),\n })\n },\n\n testField: async (input) => {\n const { deviceId } = input\n let owningAddonId: string | null = null\n if (registry) {\n owningAddonId = registry.getAddonId(deviceId)\n }\n if (!owningAddonId) {\n const persisted = await resolvePersistedById(deviceId)\n owningAddonId = persisted?.addonId ?? null\n }\n if (!owningAddonId) {\n throw new Error(`Device with id ${deviceId} not found`)\n }\n\n const dp = await this.waitDeviceProvider(owningAddonId)\n if (!dp) {\n return { status: 'ok' as const, labels: [], error: undefined }\n }\n\n if (typeof (dp as Record<string, unknown>).testCreationField !== 'function') {\n return { status: 'ok' as const, labels: [], error: undefined }\n }\n\n return dp.testCreationField({ type: DeviceType.Camera, key: input.key, value: input.value })\n },\n\n // ── Bindings — per-device capability provider map ────────────────────\n\n getBindings: async (input) => {\n const result = await this.getBindings({ deviceId: input.deviceId })\n return { deviceId: input.deviceId, entries: result.entries }\n },\n getAllBindings: async () => {\n return this.getAllBindings()\n },\n setWrapperActive: async (input) => {\n return this.setWrapperActive({\n deviceId: input.deviceId,\n capName: input.capName,\n wrapperAddonId: input.wrapperAddonId,\n active: input.active,\n })\n },\n listWrappersForCap: async (input) => this.listWrappersForCap(input),\n listBindableCapsForDeviceType: async (input) => this.listBindableCapsForDeviceType(input),\n\n // ── Device-details aggregators ──────────────────────────────────────\n\n getDeviceSettingsAggregate: async (input) => {\n return this.getDeviceAggregate(input.deviceId, 'settings')\n },\n getDeviceLiveInfoAggregate: async (input) => {\n return this.getDeviceAggregate(input.deviceId, 'live')\n },\n getDeviceAggregate: async (input) => {\n // Run both aggregations in parallel — the live + settings passes\n // enumerate the same provider set but call different methods, so\n // there's no overlap to serialize.\n const [settings, live] = await Promise.all([\n this.getDeviceAggregate(input.deviceId, 'settings'),\n this.getDeviceAggregate(input.deviceId, 'live'),\n ])\n return { settings, live }\n },\n updateDeviceField: async (input) => {\n return this.updateDeviceField({\n deviceId: input.deviceId,\n writerCapName: input.writerCapName,\n writerAddonId: input.writerAddonId,\n key: input.key,\n value: input.value,\n })\n },\n updateDeviceFieldsBatch: async (input) => {\n return this.updateDeviceFieldsBatch({ deviceId: input.deviceId, changes: input.changes })\n },\n getDeviceStatusAggregate: async (input) => this.getDeviceStatusAggregate(input),\n }\n\n this.ctx.logger.info('registered device-manager capability', { meta: { liveRegistry: registry !== null } })\n\n // Fan-out device events to ancestor scopes. Runs only when a live\n // registry is available on this node — forked workers that don't\n // own the registry rely on the hub's propagator (events cross the\n // Moleculer bridge already, propagation is idempotent with the\n // `via` guard so emitting on both sides is safe).\n if (registry) {\n this.propagator = new DeviceEventPropagator({\n eventBus: this.ctx.eventBus,\n getParentOf: (id) => registry.getById(id)?.parentDeviceId ?? null,\n logger: {\n warn: (msg, meta) => this.ctx.logger.warn(msg, meta ?? {}),\n debug: (msg, meta) => this.ctx.logger.debug(msg, meta ?? {}),\n },\n })\n this.propagator.start()\n this.ctx.logger.info('device-event-propagator started')\n }\n\n // `deviceState` cap — generic cross-process surface over the\n // hub mirror. Drivers don't implement this directly; we mount\n // the singleton here so any addon / UI can read any cap's\n // runtime-state slice without the cap having to define its own\n // getStatus / onChanged.\n const deviceStateProvider = {\n getSnapshot: async (input: { deviceId: number }): Promise<Record<string, Record<string, unknown>>> => {\n return this.snapshotForDevice(input.deviceId)\n },\n getCapSlice: async (input: { deviceId: number; capName: string }): Promise<Record<string, unknown> | null> => {\n const perCap = this.stateMirror.get(input.deviceId)\n const slice = perCap?.get(input.capName)\n return slice ? { ...slice } : null\n },\n // Whole-system warm-boot dump. Iterates the mirror once and\n // returns a deeply-cloned `{[deviceId]: {[capName]: slice}}`\n // payload so callers (SystemManager, dashboards) seed their\n // local mirror with one round-trip instead of N per-device\n // calls. Empty inner record for devices with no slices yet.\n getAllSnapshots: async (): Promise<Record<string, Record<string, Record<string, unknown>>>> => {\n const out: Record<string, Record<string, Record<string, unknown>>> = {}\n for (const [deviceId, perCap] of this.stateMirror) {\n const dev: Record<string, Record<string, unknown>> = {}\n for (const [capName, slice] of perCap) dev[capName] = { ...slice }\n out[String(deviceId)] = dev\n }\n return out\n },\n // Canonical cross-layer write — the single entrypoint every\n // caller funnels through. In-process providers reach it via\n // `BaseDevice.runtimeState.setCapState` (which delegates here);\n // system-scope addons (e.g. pipeline runner writing the motion\n // slice on phase transitions) call this cap method directly\n // through `ctx.api.deviceState.setCapSlice.mutate(...)`.\n setCapSlice: async (input: { deviceId: number; capName: string; slice: Record<string, unknown> }): Promise<void> => {\n const { deviceId, capName, slice } = input\n const persisted = await resolvePersistedById(deviceId)\n if (!persisted) throw new Error(`[device-manager] setCapSlice: unknown device id=${deviceId}`)\n // Mirror update + onChanged event are synchronous and ordered;\n // disk write is debounced so frequent writes (motion phase\n // transitions, battery pushes) collapse to one fsync per\n // RUNTIME_STATE_DEBOUNCE_MS window.\n this.applySingleCapUpdate(deviceId, capName, slice)\n this.scheduleRuntimeStateDiskWrite(deviceId, settings)\n },\n }\n\n return [\n { capability: deviceManagerCapability, provider },\n { capability: deviceStateCapability, provider: deviceStateProvider },\n ]\n }\n\n /**\n * Single-cap mirror update — diff against the current mirror,\n * persist the new slice in-memory, emit `DeviceStateChanged` for\n * this cap. No-op on identical writes (both same shape and same\n * values). Called by `setCapSlice` provider.\n */\n private applySingleCapUpdate(\n deviceId: number,\n capName: string,\n slice: Record<string, unknown>,\n ): void {\n let perCap = this.stateMirror.get(deviceId)\n if (!perCap) {\n perCap = new Map()\n this.stateMirror.set(deviceId, perCap)\n }\n const prior = perCap.get(capName)\n if (prior && shallowEqual(prior, slice)) return\n perCap.set(capName, { ...slice })\n this.emitStateChanged(deviceId, capName, slice)\n }\n\n /**\n * Debounced disk writer. Coalesces frequent writes (motion phase\n * transitions, battery pushes) into one `writeDeviceRuntimeState`\n * per `RUNTIME_STATE_DEBOUNCE_MS` window. Reads the per-device\n * blob from the live mirror at flush time so the disk picture is\n * always the latest state — no risk of writing a stale snapshot.\n */\n private scheduleRuntimeStateDiskWrite(\n deviceId: number,\n settings: NonNullable<typeof this.ctx.settings>,\n ): void {\n let slot = this.runtimeStateDebounce.get(deviceId)\n if (!slot) {\n slot = { timer: null, inFlight: null }\n this.runtimeStateDebounce.set(deviceId, slot)\n }\n if (slot.timer) return\n slot.timer = setTimeout(() => {\n slot!.timer = null\n const blob = this.snapshotForDevice(deviceId)\n const write = (async () => {\n try {\n await settings.writeDeviceRuntimeState(deviceId, blob)\n } catch (err: unknown) {\n this.ctx.logger.warn('writeDeviceRuntimeState failed', {\n tags: { deviceId },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n } finally {\n slot!.inFlight = null\n }\n })()\n slot!.inFlight = write\n }, DeviceManagerAddon.RUNTIME_STATE_DEBOUNCE_MS)\n }\n\n /**\n * One-shot mirror seed used by `loadRuntimeState` at boot so the\n * hub knows about every persisted slice without waiting for the\n * first `setCapSlice` call. No events emitted — this is\n * initial-state population, not a transition.\n */\n private seedMirror(deviceId: number, blob: Record<string, unknown>): void {\n let perCap = this.stateMirror.get(deviceId)\n if (!perCap) {\n perCap = new Map()\n this.stateMirror.set(deviceId, perCap)\n }\n for (const [capName, raw] of Object.entries(blob)) {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) continue\n perCap.set(capName, { ...raw as Record<string, unknown> })\n }\n }\n\n private snapshotForDevice(deviceId: number): Record<string, Record<string, unknown>> {\n const perCap = this.stateMirror.get(deviceId)\n if (!perCap) return {}\n const out: Record<string, Record<string, unknown>> = {}\n for (const [k, v] of perCap) out[k] = { ...v }\n return out\n }\n\n private emitStateChanged(deviceId: number, capName: string, slice: Record<string, unknown>): void {\n this.ctx.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'device', id: deviceId },\n category: EventCategory.DeviceStateChanged,\n data: { deviceId, capName, slice },\n })\n }\n\n protected async onShutdown(): Promise<void> {\n this.propagator?.stop()\n this.propagator = null\n // Flush any pending debounced runtime-state disk writes so a\n // graceful shutdown doesn't drop the latest mirror state.\n const settings = this.ctx.settings\n const pending: Promise<void>[] = []\n for (const [deviceId, slot] of this.runtimeStateDebounce) {\n if (slot.timer) {\n clearTimeout(slot.timer)\n slot.timer = null\n if (settings) {\n const blob = this.snapshotForDevice(deviceId)\n pending.push(settings.writeDeviceRuntimeState(deviceId, blob).catch((err: unknown) => {\n this.ctx.logger.warn('shutdown writeDeviceRuntimeState failed', {\n tags: { deviceId },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }))\n }\n }\n if (slot.inFlight) pending.push(slot.inFlight)\n }\n await Promise.all(pending)\n this.runtimeStateDebounce.clear()\n }\n}\n\nexport default DeviceManagerAddon\n","import type { IEventBus, EventSource, SystemEvent } from '@camstack/types'\n\n/**\n * Walks the parent chain for every device-sourced event and re-emits a\n * copy on each ancestor scope with `via[]` populated.\n *\n * Design goals:\n * - Transparent: drivers emit once on their own device scope; the\n * framework handles fan-out. Zero provider boilerplate.\n * - Anti-loop: events that already carry `via[]` are skipped (we only\n * propagate ORIGINAL emissions).\n * - Anti-cycle: the parent chain is bounded — if the device registry\n * is corrupt and has a cycle, the walker caps at `MAX_CHAIN_DEPTH`\n * and logs a warning.\n * - Lazy: parent chain is resolved on-demand per event (no cached\n * topology). The lookup is O(depth) which is ≤2 in practice.\n *\n * `via` contract (from SystemEvent.via JSDoc):\n * - `via[0]` is the originating source (the device that produced the\n * event). Subsequent entries walk up the parent chain.\n * - On the re-emission, `source` is the ancestor at that level and\n * `via[0..i]` is the prefix of the chain up to and including the\n * first N ancestors below the current one.\n *\n * Example (grandchild → parent → grandparent):\n * Original: { source: {id: 7}, data: {...}, via: undefined }\n * Re-emit 1: { source: {id: 4}, data: {...}, via: [{id: 7}] }\n * Re-emit 2: { source: {id: 1}, data: {...}, via: [{id: 7}, {id: 4}] }\n *\n * A consumer listening at `source.id === 1` receives re-emit 2 (with\n * `via` showing the chain). A consumer listening at `source.id === 7`\n * with `via === undefined` receives the original only.\n */\n\n/** Bounded walk — paranoia against corrupt device registries with cycles. */\nconst MAX_CHAIN_DEPTH = 16\n\nexport interface DeviceEventPropagatorOptions {\n readonly eventBus: IEventBus\n /**\n * Resolve the numeric parent id of a device, or `null` when the\n * device has no parent (or was deleted). The propagator injects a\n * closure over the hub's `DeviceRegistry` so the lookup is in-\n * process and O(1) per call.\n */\n readonly getParentOf: (deviceId: number) => number | null\n /** Structured logger for anti-cycle + anti-loop diagnostics. */\n readonly logger: {\n warn: (msg: string, meta?: Record<string, unknown>) => void\n debug: (msg: string, meta?: Record<string, unknown>) => void\n }\n}\n\nexport class DeviceEventPropagator {\n private unsubscribe: (() => void) | null = null\n\n constructor(private readonly opts: DeviceEventPropagatorOptions) {}\n\n start(): void {\n if (this.unsubscribe) return\n // Subscribe to ALL events — the filter is applied inline per event\n // so we can short-circuit non-device sources without walking the\n // event bus's glob engine every time.\n const unsub = this.opts.eventBus.subscribe({}, (ev) => this.handle(ev))\n this.unsubscribe = unsub\n }\n\n stop(): void {\n if (!this.unsubscribe) return\n this.unsubscribe()\n this.unsubscribe = null\n }\n\n /** Exposed for tests — lets them inject events without the full bus. */\n handle(ev: SystemEvent): void {\n if (ev.via !== undefined) return // skip propagated\n if (ev.source.type !== 'device') return // skip non-device scopes\n const rawId = ev.source.id\n const deviceId = typeof rawId === 'number' ? rawId : Number(rawId)\n if (!Number.isFinite(deviceId)) return\n\n const chain = this.resolveParentChain(deviceId)\n if (chain.length === 0) return\n\n // Build `via[]` incrementally: for the first ancestor, via = [origin];\n // for the second, via = [origin, firstAncestor]; etc.\n const origin: EventSource = ev.source\n const via: EventSource[] = [origin]\n for (const ancestorId of chain) {\n const reEmission: SystemEvent = {\n ...ev,\n source: { type: 'device', id: ancestorId },\n via: [...via],\n }\n this.opts.eventBus.emit(reEmission)\n via.push({ type: 'device', id: ancestorId })\n }\n }\n\n private resolveParentChain(deviceId: number): readonly number[] {\n const chain: number[] = []\n const seen = new Set<number>([deviceId])\n let current = this.opts.getParentOf(deviceId)\n while (current != null) {\n if (seen.has(current)) {\n this.opts.logger.warn(\n 'device-event-propagator: cycle detected in parent chain — aborting propagation',\n { tags: { deviceId }, meta: { cycleAt: current, chainSoFar: [...chain] } },\n )\n return chain\n }\n seen.add(current)\n chain.push(current)\n if (chain.length >= MAX_CHAIN_DEPTH) {\n this.opts.logger.warn(\n 'device-event-propagator: chain depth limit hit — truncating',\n { tags: { deviceId }, meta: { depth: chain.length, max: MAX_CHAIN_DEPTH } },\n )\n break\n }\n current = this.opts.getParentOf(current)\n }\n return chain\n }\n}\n"],"mappings":"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","names":["registry","key","map","metadata","sources","settings"]}
@@ -1,163 +0,0 @@
1
- // src/builtins/system-config/system-config.addon.ts
2
- import { BaseAddon, hydrateSchema, errMsg } from "@camstack/types";
3
- var SECTION_TITLES = {
4
- server: "Server",
5
- auth: "Authentication",
6
- ffmpeg: "FFmpeg"
7
- };
8
- var KEY_TO_SECTION = {
9
- // server (read-only)
10
- port: "server",
11
- host: "server",
12
- dataPath: "server",
13
- // auth
14
- tokenExpiry: "auth",
15
- // ffmpeg
16
- binaryPath: "ffmpeg",
17
- hwAccel: "ffmpeg",
18
- threadCount: "ffmpeg"
19
- };
20
- var SystemConfigAddon = class extends BaseAddon {
21
- id = "system-config";
22
- constructor() {
23
- super({});
24
- }
25
- async onInitialize() {
26
- this.ctx.logger.info("Initialized \u2014 exposes yml-backed sections via getGlobalSettings");
27
- }
28
- // ── Three-level settings API (only `getGlobalSettings` is implemented) ──
29
- buildGlobalSchema() {
30
- return {
31
- sections: [
32
- {
33
- id: "system-config-server",
34
- title: SECTION_TITLES["server"],
35
- description: "Core server connection settings \u2014 read-only, loaded from config.yaml at bootstrap.",
36
- columns: 2,
37
- fields: [
38
- {
39
- type: "info",
40
- key: "server-restart-note",
41
- label: "Restart required",
42
- content: "Server settings are read-only. Change them in config.yaml and restart.",
43
- variant: "warning"
44
- },
45
- { type: "text", key: "port", label: "Port", description: "Listening port", disabled: true },
46
- { type: "text", key: "host", label: "Host", description: "Bind address", disabled: true },
47
- { type: "text", key: "dataPath", label: "Data Path", description: "Root data directory", disabled: true, span: 2 }
48
- ]
49
- },
50
- {
51
- id: "system-config-auth",
52
- title: SECTION_TITLES["auth"],
53
- description: "Token and session settings.",
54
- columns: 1,
55
- fields: [
56
- {
57
- type: "text",
58
- key: "tokenExpiry",
59
- label: "Token Expiry",
60
- description: "JWT token lifetime (e.g. 24h, 7d, 1h)",
61
- placeholder: "24h",
62
- default: "24h"
63
- }
64
- ]
65
- },
66
- {
67
- id: "system-config-ffmpeg",
68
- title: SECTION_TITLES["ffmpeg"],
69
- description: "FFmpeg binary and hardware acceleration settings.",
70
- columns: 2,
71
- fields: [
72
- {
73
- type: "text",
74
- key: "binaryPath",
75
- label: "Binary Path",
76
- description: "Path to ffmpeg executable",
77
- placeholder: "ffmpeg",
78
- default: "ffmpeg",
79
- span: 2
80
- },
81
- {
82
- type: "select",
83
- key: "hwAccel",
84
- label: "Hardware Acceleration",
85
- description: "GPU decoding/encoding backend",
86
- default: "auto",
87
- options: [
88
- { value: "auto", label: "Auto-detect" },
89
- { value: "none", label: "None (CPU only)" },
90
- { value: "videotoolbox", label: "VideoToolbox (macOS)" },
91
- { value: "vaapi", label: "VA-API (Linux Intel/AMD)" },
92
- { value: "qsv", label: "QSV (Intel Quick Sync)" },
93
- { value: "cuda", label: "CUDA (NVIDIA)" }
94
- ]
95
- },
96
- {
97
- type: "number",
98
- key: "threadCount",
99
- label: "Thread Count",
100
- description: "0 = auto (let FFmpeg decide)",
101
- min: 0,
102
- max: 16,
103
- step: 1,
104
- default: 0
105
- }
106
- ]
107
- }
108
- ]
109
- };
110
- }
111
- async getGlobalSettings() {
112
- const schema = this.buildGlobalSchema();
113
- if (!this.ctx.settings) {
114
- return hydrateSchema(schema, {});
115
- }
116
- const sectionNames = Object.keys(SECTION_TITLES);
117
- const merged = {};
118
- for (const section of sectionNames) {
119
- try {
120
- const values = await this.ctx.settings.getSection(section);
121
- Object.assign(merged, values);
122
- } catch (err) {
123
- const msg = errMsg(err);
124
- this.ctx.logger.debug("Failed to read section", { meta: { section, error: msg } });
125
- }
126
- }
127
- return hydrateSchema(schema, merged);
128
- }
129
- async updateGlobalSettings(patch) {
130
- if (!this.ctx.settings) {
131
- throw new Error("system-config: ctx.settings not available \u2014 cannot write sections");
132
- }
133
- const perSection = /* @__PURE__ */ new Map();
134
- for (const [key, value] of Object.entries(patch)) {
135
- const section = KEY_TO_SECTION[key];
136
- if (section === void 0) {
137
- this.ctx.logger.warn("update ignored unknown key (not mapped to any section)", { meta: { key } });
138
- continue;
139
- }
140
- if (section === "server") {
141
- this.ctx.logger.warn("update ignored read-only server-section key", { meta: { key } });
142
- continue;
143
- }
144
- let bucket = perSection.get(section);
145
- if (!bucket) {
146
- bucket = {};
147
- perSection.set(section, bucket);
148
- }
149
- bucket[key] = value;
150
- }
151
- for (const [section, sectionPatch] of perSection) {
152
- await this.ctx.settings.setSection(section, sectionPatch);
153
- this.ctx.logger.info("Wrote keys to section", { meta: { section, keyCount: Object.keys(sectionPatch).length } });
154
- }
155
- }
156
- };
157
- var system_config_addon_default = SystemConfigAddon;
158
-
159
- export {
160
- SystemConfigAddon,
161
- system_config_addon_default
162
- };
163
- //# sourceMappingURL=chunk-QT57H266.mjs.map