@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,477 +1,488 @@
1
- // src/builtins/snapshot/snapshot.addon.ts
2
- import { execFile } from "child_process";
3
- import { DeviceFeature, DeviceType, snapshotCapability } from "@camstack/types";
4
- import { BaseAddon, errMsg, streamQualityLabel } from "@camstack/types";
1
+ import { execFile } from "node:child_process";
2
+ import { BaseAddon, DeviceFeature, DeviceType, errMsg, snapshotCapability, streamQualityLabel } from "@camstack/types";
3
+ //#region src/builtins/snapshot/snapshot.addon.ts
4
+ /** Default cache window for non-battery cams (seconds). 10s feels live. */
5
5
  var NON_BATTERY_DEFAULT_MAX_AGE_S = 10;
6
+ /** Default cache window for battery cams (seconds). 1h ≈ "don't wake the cam unless asked". */
6
7
  var BATTERY_DEFAULT_MAX_AGE_S = 3600;
8
+ /**
9
+ * SnapshotAddon — wrapper over the `snapshot` capability.
10
+ *
11
+ * Activated per-device (toggleable by user; default active). When active,
12
+ * caches fresh snapshots in memory. On cache miss, delegates to the native
13
+ * provider for this device via `ctx.getNativeProvider(snapshotCapability, id)`.
14
+ *
15
+ * No silent fallback — if the native fails, propagate the error up. Stale
16
+ * cache is returned ONLY when the native throws AND a stale entry exists,
17
+ * and the failure is logged so the fallback is never silent.
18
+ *
19
+ * Frame-grab fallback: when the native provider is absent (or returns null /
20
+ * throws), we ask the `stream-broker` capability for the device's RTSP
21
+ * restream URL and pipe one JPEG out of ffmpeg. stream-broker intentionally
22
+ * does NOT expose a `grabFrame` method — the orchestration (native-first,
23
+ * ffmpeg fallback, caching) is this addon's concern; stream-broker only
24
+ * publishes stream endpoints.
25
+ */
7
26
  var SnapshotAddon = class extends BaseAddon {
8
- cache = /* @__PURE__ */ new Map();
9
- constructor() {
10
- super({
11
- staleTtlMs: 6e4
12
- });
13
- }
14
- async onInitialize() {
15
- this.ctx.logger.info("Snapshot wrapper initialized");
16
- const provider = {
17
- getSnapshot: (input) => this.getSnapshot(input),
18
- invalidateCache: (input) => this.invalidateCache(input),
19
- // DeviceSettingsContribution surface — numeric deviceId end-to-end.
20
- getDeviceSettingsContribution: (input) => this.buildDeviceSettingsContribution(input.deviceId),
21
- getDeviceLiveContribution: async () => null,
22
- applyDeviceSettingsPatch: (input) => this.saveDeviceSettingsPatch(input.deviceId, input.patch),
23
- // Status surface — derives a diagnostic snapshot from the cache
24
- // bookkeeping (lastCapturedAt, cacheAgeMs, lastBytes, lastStreamId).
25
- // Returns null when the device has never been captured.
26
- getStatus: async (input) => this.getStatus(input.deviceId)
27
- };
28
- return [{
29
- capability: snapshotCapability,
30
- provider,
31
- kind: "wrapper",
32
- defaultActive: true
33
- }];
34
- }
35
- async onShutdown() {
36
- this.cache.clear();
37
- }
38
- globalSettingsSchema() {
39
- return this.schema({
40
- sections: [{
41
- id: "snapshot-cache",
42
- title: "Snapshot Cache",
43
- description: "Stale fallback when live capture fails. Per-device freshness is configured under Device \u2192 Snapshot \u2192 Max cache age.",
44
- columns: 1,
45
- fields: [
46
- this.field({
47
- type: "number",
48
- key: "staleTtlMs",
49
- label: "Stale fallback TTL (ms)",
50
- description: "If live capture fails, cached snapshot younger than this is still returned.",
51
- min: 0,
52
- max: 36e5,
53
- step: 1e3,
54
- default: 6e4
55
- })
56
- ]
57
- }]
58
- });
59
- }
60
- // ── Capability methods ────────────────────────────────────────────────
61
- async getSnapshot(input) {
62
- const { deviceId, force } = input;
63
- const meta = await this.lookupDeviceMeta(deviceId);
64
- const deviceName = meta?.name;
65
- const isBatteryDevice = meta?.isBattery ?? false;
66
- const log = this.ctx.logger.withTags({ deviceId, ...deviceName ? { deviceName } : {} });
67
- const now = Date.now();
68
- const hit = this.cache.get(deviceId);
69
- const prefs = await this.readDeviceSettings(deviceId).catch(() => ({}));
70
- const rawPref = prefs.snapshotStreamId;
71
- const effectiveStreamId = input.streamId ?? (rawPref && rawPref !== "auto" ? rawPref : void 0);
72
- if (prefs.snapshotDebug) {
73
- log.info("debug on", { tags: { deviceId }, meta: { stream: effectiveStreamId ?? "auto" } });
74
- }
75
- const defaultMaxAgeS = isBatteryDevice ? BATTERY_DEFAULT_MAX_AGE_S : NON_BATTERY_DEFAULT_MAX_AGE_S;
76
- const effectiveMaxAgeMs = (typeof prefs.snapshotMaxAgeS === "number" && prefs.snapshotMaxAgeS >= 0 ? prefs.snapshotMaxAgeS : defaultMaxAgeS) * 1e3;
77
- if (!force && hit && now - hit.ts < effectiveMaxAgeMs) {
78
- if (prefs.snapshotDebug) {
79
- log.debug("snapshot: cache hit", {
80
- tags: { deviceId },
81
- meta: { ageMs: now - hit.ts, maxAgeMs: effectiveMaxAgeMs, isBattery: isBatteryDevice }
82
- });
83
- }
84
- return hit.data;
85
- }
86
- let nativeError = null;
87
- let nativeAbsent = false;
88
- try {
89
- const native = this.ctx.getNativeProvider(snapshotCapability, deviceId);
90
- if (native) {
91
- const result = await native.getSnapshot(input);
92
- if (result) {
93
- this.cache.set(deviceId, { data: result, ts: now, streamId: effectiveStreamId ?? null });
94
- return result;
95
- }
96
- } else {
97
- nativeAbsent = true;
98
- }
99
- } catch (err) {
100
- const msg = errMsg(err);
101
- if (isAbsentNativeError(msg)) {
102
- nativeAbsent = true;
103
- log.debug("native snapshot absent", { tags: { deviceId }, meta: { error: msg } });
104
- } else {
105
- nativeError = err;
106
- log.warn("native snapshot failed", { tags: { deviceId }, meta: { error: msg } });
107
- }
108
- }
109
- const skipBrokerForBattery = isBatteryDevice && nativeAbsent && !await this.hasStreamingBrokerForDevice(deviceId);
110
- if (!skipBrokerForBattery) {
111
- try {
112
- const fallback = await this.grabFrameFromBroker(deviceId, effectiveStreamId);
113
- if (fallback) {
114
- this.cache.set(deviceId, { data: fallback, ts: now, streamId: effectiveStreamId ?? null });
115
- return fallback;
116
- }
117
- } catch (err) {
118
- log.warn("stream-broker snapshot fallback failed", { tags: { deviceId }, meta: { error: errMsg(err) } });
119
- }
120
- } else {
121
- log.debug("snapshot: skipping broker fallback \u2014 battery device with absent native and no streaming broker", {
122
- tags: { deviceId }
123
- });
124
- }
125
- if (hit) {
126
- const ageMs = now - hit.ts;
127
- if (ageMs > this.config.staleTtlMs) {
128
- log.warn("snapshot: all live paths failed \u2014 serving stale cache", { tags: { deviceId }, meta: { ageMs } });
129
- }
130
- return hit.data;
131
- }
132
- if (nativeError) throw nativeError;
133
- if (nativeAbsent) return null;
134
- return null;
135
- }
136
- /**
137
- * Tell apart "native provider isn't registered for this device" from
138
- * "native provider ran and threw a real error". The former is the steady
139
- * state for cameras without a vendor snapshot endpoint and should not
140
- * propagate as a 500; the latter should.
141
- */
142
- /**
143
- * Pull one JPEG from the device's stream-broker RTSP restream using
144
- * a short-lived ffmpeg invocation.
145
- *
146
- * Stream selection strategy (picks the broker that won't stall):
147
- * 1. Explicit `preferredStreamId` (user set in per-device settings)
148
- * always honoured, even if currently idle. Operator choice
149
- * wins.
150
- * 2. Auto: highest-quality broker currently in `streaming` state.
151
- * This is the whole point prefer the stream that has active
152
- * subscribers (usually `low` for detection, but `mid`/`high`
153
- * if WebRTC is watching) so ffmpeg hits a warm pipe and
154
- * doesn't race the broker's resume.
155
- * 3. Fallback: highest-quality enabled entry regardless of status
156
- * (will wake a suspended broker — retry-guarded against the
157
- * cold-start error).
158
- *
159
- * The stream-broker auto-suspends idle streams on the "no demand"
160
- * signal; snapshots used to default to `high` which was often the
161
- * first to go idle, racing every snapshot with a broker resume.
162
- * Now we ask the orchestrator of streams which one is warm and grab
163
- * from there.
164
- */
165
- async grabFrameFromBroker(deviceId, preferredStreamId) {
166
- const dev = await this.ctx.fetchDevice(deviceId);
167
- const prefix = `${deviceId}/`;
168
- const [deviceEntries, profileSlots] = await Promise.all([
169
- dev.cameraStreams?.getRtspEntries({}) ?? [],
170
- dev.cameraStreams?.getBrokerStreams({}) ?? []
171
- ]);
172
- const usable = deviceEntries.filter((e) => e.enabled && !!e.url);
173
- if (usable.length === 0) return null;
174
- if (preferredStreamId && preferredStreamId !== "auto") {
175
- const explicit = usable.find((e) => e.brokerId === `${prefix}${preferredStreamId}`);
176
- if (explicit) {
177
- const grabbed = await this.runGrabWithResumeRetry(explicit.url, deviceId);
178
- if (grabbed) return grabbed;
179
- this.ctx.logger.debug("grabFrame: explicit stream failed \u2014 falling back to auto", { meta: { preferredStreamId } });
180
- }
181
- }
182
- const ranked = [...usable].sort(
183
- (a, b) => qualityRank(b.brokerId, prefix) - qualityRank(a.brokerId, prefix)
184
- );
185
- const statusByBrokerId = /* @__PURE__ */ new Map();
186
- for (const slot of profileSlots) statusByBrokerId.set(slot.brokerId, slot.status);
187
- const statuses = ranked.map((e) => ({ entry: e, status: statusByBrokerId.get(e.brokerId) ?? "idle" }));
188
- const warm = statuses.find((s) => s.status === "streaming");
189
- if (warm) {
190
- const grabbed = await this.runGrabWithResumeRetry(warm.entry.url, deviceId);
191
- if (grabbed) return grabbed;
192
- }
193
- for (const { entry } of statuses) {
194
- const grabbed = await this.runGrabWithResumeRetry(entry.url, deviceId).catch(() => null);
195
- if (grabbed) return grabbed;
196
- }
197
- return null;
198
- }
199
- /**
200
- * Ffmpeg grab with one retry on the broker-cold-start error
201
- * signature. Covers the window between "client connected" and
202
- * "first keyframe" when a suspended broker resumes.
203
- */
204
- async runGrabWithResumeRetry(url, deviceId) {
205
- let buf;
206
- try {
207
- buf = await runFfmpegFrameGrab(url, 15e3);
208
- } catch (err) {
209
- const msg = errMsg(err);
210
- if (isBrokerColdError(msg)) {
211
- this.ctx.logger.debug("grabFrame: broker-resume race \u2014 retrying in 1500ms", { tags: { deviceId } });
212
- await new Promise((r) => setTimeout(r, 1500));
213
- buf = await runFfmpegFrameGrab(url, 15e3);
214
- } else {
215
- throw err;
216
- }
217
- }
218
- if (buf.length === 0) return null;
219
- return { base64: buf.toString("base64"), contentType: "image/jpeg" };
220
- }
221
- async invalidateCache(input) {
222
- this.cache.delete(input.deviceId);
223
- }
224
- /**
225
- * Non-throwing probe of the device's battery cap. Returns true only
226
- * when a battery native is registered AND its current status says
227
- * `sleeping: true`. Any error (no provider, native absent, getStatus
228
- * missing, RPC timeout) is swallowed and treated as "awake" — we'd
229
- * rather pay a wake-up than strand the caller on a cache that's
230
- * semantically stale. Debug-logged for observability.
231
- */
232
- /**
233
- * True when at least one of the device's brokers is actively
234
- * streaming (status === 'streaming'). Used by the battery-cam guard
235
- * around `grabFrameFromBroker` to allow the fallback ONLY when
236
- * grabbing a frame is free (a consumer is already keeping the
237
- * stream warm). When everything is suspended, the fallback would
238
- * dial the camera and wake it defeats the sleeping cache.
239
- */
240
- async hasStreamingBrokerForDevice(deviceId) {
241
- try {
242
- const dev = await this.ctx.fetchDevice(deviceId);
243
- const slots = await dev.cameraStreams?.getBrokerStreams({}) ?? [];
244
- return slots.some((s) => s.status === "streaming");
245
- } catch {
246
- return false;
247
- }
248
- }
249
- /**
250
- * Diagnostic status for the `status` auto-injected cap method. Reports
251
- * the cache bookkeeping for this device — when the last snapshot was
252
- * captured, how stale the cached image is, its size, and which stream
253
- * was used. Returns null when the device has never been captured
254
- * (cache miss) since the addon started.
255
- */
256
- async getStatus(deviceId) {
257
- const hit = this.cache.get(deviceId);
258
- if (!hit) return null;
259
- return {
260
- lastCapturedAt: hit.ts,
261
- cacheAgeMs: Date.now() - hit.ts,
262
- lastBytes: hit.data.base64.length,
263
- // approx bytes = base64 length * 3/4, but length is stable enough
264
- lastStreamId: hit.streamId
265
- };
266
- }
267
- // ── DeviceSettingsContribution ────────────────────────────────────────
268
- //
269
- // Snapshot carries two per-device knobs:
270
- // - `snapshotStreamId` which stream to prefer when grabbing frames
271
- // (empty = auto / default "main")
272
- // - `snapshotDebug` extra logging for troubleshooting snapshot paths
273
- //
274
- // Storage uses the addon's own per-device store (`ctx.settings
275
- // .writeDeviceStore`), not the device's config. The schema isn't
276
- // captured in a Zod object: the keys are optional and the UI layer
277
- // validates shapes.
278
- async readDeviceSettings(deviceId) {
279
- if (!this.ctx.settings) return {};
280
- const raw = await this.ctx.settings.readDeviceStore(deviceId);
281
- const rawAge = raw["snapshotMaxAgeS"];
282
- const maxAgeS = typeof rawAge === "number" && rawAge >= 0 && Number.isFinite(rawAge) ? rawAge : void 0;
283
- return {
284
- snapshotStreamId: typeof raw["snapshotStreamId"] === "string" ? raw["snapshotStreamId"] : void 0,
285
- snapshotDebug: raw["snapshotDebug"] === true,
286
- ...maxAgeS !== void 0 ? { snapshotMaxAgeS: maxAgeS } : {}
287
- };
288
- }
289
- async buildDeviceSettingsContribution(deviceId) {
290
- const meta = await this.lookupDeviceMeta(deviceId);
291
- if (meta && meta.type !== DeviceType.Camera) return null;
292
- const current = await this.readDeviceSettings(deviceId);
293
- const streamOptions = await this.getStreamOptions(deviceId);
294
- const isBattery = await this.isDeviceBattery(deviceId);
295
- const defaultMaxAgeS = isBattery ? BATTERY_DEFAULT_MAX_AGE_S : NON_BATTERY_DEFAULT_MAX_AGE_S;
296
- return {
297
- sections: [{
298
- id: "snapshot-preferences",
299
- title: "Snapshot",
300
- tab: "snapshot",
301
- order: 60,
302
- fields: [
303
- {
304
- type: "select",
305
- key: "snapshotStreamId",
306
- label: "Preferred stream",
307
- description: "Stream used when grabbing a snapshot",
308
- options: streamOptions,
309
- required: true,
310
- value: current.snapshotStreamId || "auto"
311
- },
312
- {
313
- type: "number",
314
- key: "snapshotMaxAgeS",
315
- label: "Max cache age (s)",
316
- description: `Serve cached snapshot up to this age before re-capturing. Default ${defaultMaxAgeS}s for ${isBattery ? "battery cams (avoids gratuitous wake-ups)" : "non-battery cams (live-feel)"}. The UI refresh button always forces a fresh capture regardless of this value.`,
317
- min: 0,
318
- max: 24 * 3600,
319
- step: 1,
320
- value: current.snapshotMaxAgeS ?? defaultMaxAgeS
321
- },
322
- {
323
- type: "boolean",
324
- key: "snapshotDebug",
325
- label: "Debug logging",
326
- description: "Log stream selection and timing details for this device.",
327
- value: current.snapshotDebug ?? false
328
- }
329
- ]
330
- }]
331
- };
332
- }
333
- /**
334
- * Single-trip device lookup against device-manager. Returns the
335
- * fields the wrapper actually consults — name (logging) + battery
336
- * flag (cache window + broker-fallback gate). Sourced from the
337
- * device-manager registry rather than the battery cap so the answer
338
- * survives a momentarily-unreachable provider (the very condition
339
- * we're trying to be resilient to).
340
- *
341
- * Logged at debug + null return on failure: every call site already
342
- * has a sensible fallback path (cache hit, conservative default, …),
343
- * so we don't want a transient device-manager hiccup to throw.
344
- */
345
- async lookupDeviceMeta(deviceId) {
346
- const api = this.ctx.api;
347
- if (!api) return null;
348
- try {
349
- const found = await api.deviceManager.getDevice.query({ deviceId });
350
- if (!found) return null;
351
- const features = found.features ?? [];
352
- const rawType = found.type;
353
- return {
354
- ...found.name ? { name: found.name } : {},
355
- isBattery: features.includes(DeviceFeature.BatteryOperated),
356
- ...rawType ? { type: rawType } : {}
357
- };
358
- } catch (err) {
359
- this.ctx.logger.debug("deviceManager.getDevice failed during snapshot", {
360
- tags: { deviceId },
361
- meta: { error: err instanceof Error ? err.message : String(err) }
362
- });
363
- return null;
364
- }
365
- }
366
- /** Settings-UI helper — battery flag drives the default max-age in the field description. */
367
- async isDeviceBattery(deviceId) {
368
- return (await this.lookupDeviceMeta(deviceId))?.isBattery ?? false;
369
- }
370
- async getStreamOptions(deviceId) {
371
- const prefix = `${deviceId}/`;
372
- try {
373
- const dev = await this.ctx.fetchDevice(deviceId);
374
- const entries = await dev.cameraStreams?.getRtspEntries({}) ?? [];
375
- const streamIds = entries.filter((e) => e.enabled).map((e) => e.brokerId.slice(prefix.length));
376
- return [
377
- { value: "auto", label: "Auto" },
378
- ...streamIds.map((id) => ({ value: id, label: streamQualityLabel(id) }))
379
- ];
380
- } catch (err) {
381
- this.ctx.logger.error("getStreamOptions failed", { tags: { deviceId }, meta: { error: errMsg(err) } });
382
- return [{ value: "auto", label: "Auto" }];
383
- }
384
- }
385
- async saveDeviceSettingsPatch(deviceId, patch) {
386
- if (!this.ctx.settings) {
387
- throw new Error("[snapshot] settings store unavailable \u2014 cannot persist per-device settings");
388
- }
389
- const current = await this.ctx.settings.readDeviceStore(deviceId);
390
- const next = { ...current };
391
- if ("snapshotStreamId" in patch) {
392
- const v = patch["snapshotStreamId"];
393
- next["snapshotStreamId"] = typeof v === "string" && v.trim().length > 0 ? v.trim() : "";
394
- }
395
- if ("snapshotDebug" in patch) {
396
- next["snapshotDebug"] = patch["snapshotDebug"] === true;
397
- }
398
- if ("snapshotMaxAgeS" in patch) {
399
- const v = patch["snapshotMaxAgeS"];
400
- if (typeof v === "number" && Number.isFinite(v) && v >= 0) {
401
- next["snapshotMaxAgeS"] = v;
402
- } else {
403
- delete next["snapshotMaxAgeS"];
404
- }
405
- }
406
- await this.ctx.settings.writeDeviceStore(deviceId, next);
407
- this.cache.delete(deviceId);
408
- return { success: true };
409
- }
27
+ cache = /* @__PURE__ */ new Map();
28
+ constructor() {
29
+ super({ staleTtlMs: 6e4 });
30
+ }
31
+ async onInitialize() {
32
+ this.ctx.logger.info("Snapshot wrapper initialized");
33
+ return [{
34
+ capability: snapshotCapability,
35
+ provider: {
36
+ getSnapshot: (input) => this.getSnapshot(input),
37
+ invalidateCache: (input) => this.invalidateCache(input),
38
+ getDeviceSettingsContribution: (input) => this.buildDeviceSettingsContribution(input.deviceId),
39
+ getDeviceLiveContribution: async () => null,
40
+ applyDeviceSettingsPatch: (input) => this.saveDeviceSettingsPatch(input.deviceId, input.patch),
41
+ getStatus: async (input) => this.getStatus(input.deviceId)
42
+ },
43
+ kind: "wrapper",
44
+ defaultActive: true
45
+ }];
46
+ }
47
+ async onShutdown() {
48
+ this.cache.clear();
49
+ }
50
+ globalSettingsSchema() {
51
+ return this.schema({ sections: [{
52
+ id: "snapshot-cache",
53
+ title: "Snapshot Cache",
54
+ description: "Stale fallback when live capture fails. Per-device freshness is configured under Device → Snapshot → Max cache age.",
55
+ columns: 1,
56
+ fields: [this.field({
57
+ type: "number",
58
+ key: "staleTtlMs",
59
+ label: "Stale fallback TTL (ms)",
60
+ description: "If live capture fails, cached snapshot younger than this is still returned.",
61
+ min: 0,
62
+ max: 36e5,
63
+ step: 1e3,
64
+ default: 6e4
65
+ })]
66
+ }] });
67
+ }
68
+ async getSnapshot(input) {
69
+ const { deviceId, force } = input;
70
+ const meta = await this.lookupDeviceMeta(deviceId);
71
+ const deviceName = meta?.name;
72
+ const isBatteryDevice = meta?.isBattery ?? false;
73
+ const log = this.ctx.logger.withTags({
74
+ deviceId,
75
+ ...deviceName ? { deviceName } : {}
76
+ });
77
+ const now = Date.now();
78
+ const hit = this.cache.get(deviceId);
79
+ const prefs = await this.readDeviceSettings(deviceId).catch(() => ({}));
80
+ const rawPref = prefs.snapshotStreamId;
81
+ const effectiveStreamId = input.streamId ?? (rawPref && rawPref !== "auto" ? rawPref : void 0);
82
+ if (prefs.snapshotDebug) log.info("debug on", {
83
+ tags: { deviceId },
84
+ meta: { stream: effectiveStreamId ?? "auto" }
85
+ });
86
+ const defaultMaxAgeS = isBatteryDevice ? BATTERY_DEFAULT_MAX_AGE_S : NON_BATTERY_DEFAULT_MAX_AGE_S;
87
+ const effectiveMaxAgeMs = (typeof prefs.snapshotMaxAgeS === "number" && prefs.snapshotMaxAgeS >= 0 ? prefs.snapshotMaxAgeS : defaultMaxAgeS) * 1e3;
88
+ if (!force && hit && now - hit.ts < effectiveMaxAgeMs) {
89
+ if (prefs.snapshotDebug) log.debug("snapshot: cache hit", {
90
+ tags: { deviceId },
91
+ meta: {
92
+ ageMs: now - hit.ts,
93
+ maxAgeMs: effectiveMaxAgeMs,
94
+ isBattery: isBatteryDevice
95
+ }
96
+ });
97
+ return hit.data;
98
+ }
99
+ let nativeError = null;
100
+ let nativeAbsent = false;
101
+ try {
102
+ const native = this.ctx.getNativeProvider(snapshotCapability, deviceId);
103
+ if (native) {
104
+ const result = await native.getSnapshot(input);
105
+ if (result) {
106
+ this.cache.set(deviceId, {
107
+ data: result,
108
+ ts: now,
109
+ streamId: effectiveStreamId ?? null
110
+ });
111
+ return result;
112
+ }
113
+ } else nativeAbsent = true;
114
+ } catch (err) {
115
+ const msg = errMsg(err);
116
+ if (isAbsentNativeError(msg)) {
117
+ nativeAbsent = true;
118
+ log.debug("native snapshot absent", {
119
+ tags: { deviceId },
120
+ meta: { error: msg }
121
+ });
122
+ } else {
123
+ nativeError = err;
124
+ log.warn("native snapshot failed", {
125
+ tags: { deviceId },
126
+ meta: { error: msg }
127
+ });
128
+ }
129
+ }
130
+ if (!(isBatteryDevice && nativeAbsent && !await this.hasStreamingBrokerForDevice(deviceId))) try {
131
+ const fallback = await this.grabFrameFromBroker(deviceId, effectiveStreamId);
132
+ if (fallback) {
133
+ this.cache.set(deviceId, {
134
+ data: fallback,
135
+ ts: now,
136
+ streamId: effectiveStreamId ?? null
137
+ });
138
+ return fallback;
139
+ }
140
+ } catch (err) {
141
+ log.warn("stream-broker snapshot fallback failed", {
142
+ tags: { deviceId },
143
+ meta: { error: errMsg(err) }
144
+ });
145
+ }
146
+ else log.debug("snapshot: skipping broker fallback — battery device with absent native and no streaming broker", { tags: { deviceId } });
147
+ if (hit) {
148
+ const ageMs = now - hit.ts;
149
+ if (ageMs > this.config.staleTtlMs) log.warn("snapshot: all live paths failed — serving stale cache", {
150
+ tags: { deviceId },
151
+ meta: { ageMs }
152
+ });
153
+ return hit.data;
154
+ }
155
+ if (nativeError) throw nativeError;
156
+ if (nativeAbsent) return null;
157
+ return null;
158
+ }
159
+ /**
160
+ * Tell apart "native provider isn't registered for this device" from
161
+ * "native provider ran and threw a real error". The former is the steady
162
+ * state for cameras without a vendor snapshot endpoint and should not
163
+ * propagate as a 500; the latter should.
164
+ */
165
+ /**
166
+ * Pull one JPEG from the device's stream-broker RTSP restream using
167
+ * a short-lived ffmpeg invocation.
168
+ *
169
+ * Stream selection strategy (picks the broker that won't stall):
170
+ * 1. Explicit `preferredStreamId` (user set in per-device settings)
171
+ * always honoured, even if currently idle. Operator choice
172
+ * wins.
173
+ * 2. Auto: highest-quality broker currently in `streaming` state.
174
+ * This is the whole point prefer the stream that has active
175
+ * subscribers (usually `low` for detection, but `mid`/`high`
176
+ * if WebRTC is watching) so ffmpeg hits a warm pipe and
177
+ * doesn't race the broker's resume.
178
+ * 3. Fallback: highest-quality enabled entry regardless of status
179
+ * (will wake a suspended broker retry-guarded against the
180
+ * cold-start error).
181
+ *
182
+ * The stream-broker auto-suspends idle streams on the "no demand"
183
+ * signal; snapshots used to default to `high` which was often the
184
+ * first to go idle, racing every snapshot with a broker resume.
185
+ * Now we ask the orchestrator of streams which one is warm and grab
186
+ * from there.
187
+ */
188
+ async grabFrameFromBroker(deviceId, preferredStreamId) {
189
+ const dev = await this.ctx.fetchDevice(deviceId);
190
+ const prefix = `${deviceId}/`;
191
+ const [deviceEntries, profileSlots] = await Promise.all([dev.cameraStreams?.getRtspEntries({}) ?? [], dev.cameraStreams?.getBrokerStreams({}) ?? []]);
192
+ const usable = deviceEntries.filter((e) => e.enabled && !!e.url);
193
+ if (usable.length === 0) return null;
194
+ if (preferredStreamId && preferredStreamId !== "auto") {
195
+ const explicit = usable.find((e) => e.brokerId === `${prefix}${preferredStreamId}`);
196
+ if (explicit) {
197
+ const grabbed = await this.runGrabWithResumeRetry(explicit.url, deviceId);
198
+ if (grabbed) return grabbed;
199
+ this.ctx.logger.debug("grabFrame: explicit stream failed — falling back to auto", { meta: { preferredStreamId } });
200
+ }
201
+ }
202
+ const ranked = [...usable].sort((a, b) => qualityRank(b.brokerId, prefix) - qualityRank(a.brokerId, prefix));
203
+ const statusByBrokerId = /* @__PURE__ */ new Map();
204
+ for (const slot of profileSlots) statusByBrokerId.set(slot.brokerId, slot.status);
205
+ const statuses = ranked.map((e) => ({
206
+ entry: e,
207
+ status: statusByBrokerId.get(e.brokerId) ?? "idle"
208
+ }));
209
+ const warm = statuses.find((s) => s.status === "streaming");
210
+ if (warm) {
211
+ const grabbed = await this.runGrabWithResumeRetry(warm.entry.url, deviceId);
212
+ if (grabbed) return grabbed;
213
+ }
214
+ for (const { entry } of statuses) {
215
+ const grabbed = await this.runGrabWithResumeRetry(entry.url, deviceId).catch(() => null);
216
+ if (grabbed) return grabbed;
217
+ }
218
+ return null;
219
+ }
220
+ /**
221
+ * Ffmpeg grab with one retry on the broker-cold-start error
222
+ * signature. Covers the window between "client connected" and
223
+ * "first keyframe" when a suspended broker resumes.
224
+ */
225
+ async runGrabWithResumeRetry(url, deviceId) {
226
+ let buf;
227
+ try {
228
+ buf = await runFfmpegFrameGrab(url, 15e3);
229
+ } catch (err) {
230
+ if (isBrokerColdError(errMsg(err))) {
231
+ this.ctx.logger.debug("grabFrame: broker-resume race retrying in 1500ms", { tags: { deviceId } });
232
+ await new Promise((r) => setTimeout(r, 1500));
233
+ buf = await runFfmpegFrameGrab(url, 15e3);
234
+ } else throw err;
235
+ }
236
+ if (buf.length === 0) return null;
237
+ return {
238
+ base64: buf.toString("base64"),
239
+ contentType: "image/jpeg"
240
+ };
241
+ }
242
+ async invalidateCache(input) {
243
+ this.cache.delete(input.deviceId);
244
+ }
245
+ /**
246
+ * Non-throwing probe of the device's battery cap. Returns true only
247
+ * when a battery native is registered AND its current status says
248
+ * `sleeping: true`. Any error (no provider, native absent, getStatus
249
+ * missing, RPC timeout) is swallowed and treated as "awake" — we'd
250
+ * rather pay a wake-up than strand the caller on a cache that's
251
+ * semantically stale. Debug-logged for observability.
252
+ */
253
+ /**
254
+ * True when at least one of the device's brokers is actively
255
+ * streaming (status === 'streaming'). Used by the battery-cam guard
256
+ * around `grabFrameFromBroker` to allow the fallback ONLY when
257
+ * grabbing a frame is free (a consumer is already keeping the
258
+ * stream warm). When everything is suspended, the fallback would
259
+ * dial the camera and wake it — defeats the sleeping cache.
260
+ */
261
+ async hasStreamingBrokerForDevice(deviceId) {
262
+ try {
263
+ return (await (await this.ctx.fetchDevice(deviceId)).cameraStreams?.getBrokerStreams({}) ?? []).some((s) => s.status === "streaming");
264
+ } catch {
265
+ return false;
266
+ }
267
+ }
268
+ /**
269
+ * Diagnostic status for the `status` auto-injected cap method. Reports
270
+ * the cache bookkeeping for this device — when the last snapshot was
271
+ * captured, how stale the cached image is, its size, and which stream
272
+ * was used. Returns null when the device has never been captured
273
+ * (cache miss) since the addon started.
274
+ */
275
+ async getStatus(deviceId) {
276
+ const hit = this.cache.get(deviceId);
277
+ if (!hit) return null;
278
+ return {
279
+ lastCapturedAt: hit.ts,
280
+ cacheAgeMs: Date.now() - hit.ts,
281
+ lastBytes: hit.data.base64.length,
282
+ lastStreamId: hit.streamId
283
+ };
284
+ }
285
+ async readDeviceSettings(deviceId) {
286
+ if (!this.ctx.settings) return {};
287
+ const raw = await this.ctx.settings.readDeviceStore(deviceId);
288
+ const rawAge = raw["snapshotMaxAgeS"];
289
+ const maxAgeS = typeof rawAge === "number" && rawAge >= 0 && Number.isFinite(rawAge) ? rawAge : void 0;
290
+ return {
291
+ snapshotStreamId: typeof raw["snapshotStreamId"] === "string" ? raw["snapshotStreamId"] : void 0,
292
+ snapshotDebug: raw["snapshotDebug"] === true,
293
+ ...maxAgeS !== void 0 ? { snapshotMaxAgeS: maxAgeS } : {}
294
+ };
295
+ }
296
+ async buildDeviceSettingsContribution(deviceId) {
297
+ const meta = await this.lookupDeviceMeta(deviceId);
298
+ if (meta && meta.type !== DeviceType.Camera) return null;
299
+ const current = await this.readDeviceSettings(deviceId);
300
+ const streamOptions = await this.getStreamOptions(deviceId);
301
+ const isBattery = await this.isDeviceBattery(deviceId);
302
+ const defaultMaxAgeS = isBattery ? BATTERY_DEFAULT_MAX_AGE_S : NON_BATTERY_DEFAULT_MAX_AGE_S;
303
+ return { sections: [{
304
+ id: "snapshot-preferences",
305
+ title: "Snapshot",
306
+ tab: "snapshot",
307
+ order: 60,
308
+ fields: [
309
+ {
310
+ type: "select",
311
+ key: "snapshotStreamId",
312
+ label: "Preferred stream",
313
+ description: "Stream used when grabbing a snapshot",
314
+ options: streamOptions,
315
+ required: true,
316
+ value: current.snapshotStreamId || "auto"
317
+ },
318
+ {
319
+ type: "number",
320
+ key: "snapshotMaxAgeS",
321
+ label: "Max cache age (s)",
322
+ description: `Serve cached snapshot up to this age before re-capturing. Default ${defaultMaxAgeS}s for ${isBattery ? "battery cams (avoids gratuitous wake-ups)" : "non-battery cams (live-feel)"}. The UI refresh button always forces a fresh capture regardless of this value.`,
323
+ min: 0,
324
+ max: 24 * 3600,
325
+ step: 1,
326
+ value: current.snapshotMaxAgeS ?? defaultMaxAgeS
327
+ },
328
+ {
329
+ type: "boolean",
330
+ key: "snapshotDebug",
331
+ label: "Debug logging",
332
+ description: "Log stream selection and timing details for this device.",
333
+ value: current.snapshotDebug ?? false
334
+ }
335
+ ]
336
+ }] };
337
+ }
338
+ /**
339
+ * Single-trip device lookup against device-manager. Returns the
340
+ * fields the wrapper actually consults — name (logging) + battery
341
+ * flag (cache window + broker-fallback gate). Sourced from the
342
+ * device-manager registry rather than the battery cap so the answer
343
+ * survives a momentarily-unreachable provider (the very condition
344
+ * we're trying to be resilient to).
345
+ *
346
+ * Logged at debug + null return on failure: every call site already
347
+ * has a sensible fallback path (cache hit, conservative default, …),
348
+ * so we don't want a transient device-manager hiccup to throw.
349
+ */
350
+ async lookupDeviceMeta(deviceId) {
351
+ const api = this.ctx.api;
352
+ if (!api) return null;
353
+ try {
354
+ const found = await api.deviceManager.getDevice.query({ deviceId });
355
+ if (!found) return null;
356
+ const features = found.features ?? [];
357
+ const rawType = found.type;
358
+ return {
359
+ ...found.name ? { name: found.name } : {},
360
+ isBattery: features.includes(DeviceFeature.BatteryOperated),
361
+ ...rawType ? { type: rawType } : {}
362
+ };
363
+ } catch (err) {
364
+ this.ctx.logger.debug("deviceManager.getDevice failed during snapshot", {
365
+ tags: { deviceId },
366
+ meta: { error: err instanceof Error ? err.message : String(err) }
367
+ });
368
+ return null;
369
+ }
370
+ }
371
+ /** Settings-UI helper — battery flag drives the default max-age in the field description. */
372
+ async isDeviceBattery(deviceId) {
373
+ return (await this.lookupDeviceMeta(deviceId))?.isBattery ?? false;
374
+ }
375
+ async getStreamOptions(deviceId) {
376
+ const prefix = `${deviceId}/`;
377
+ try {
378
+ return [{
379
+ value: "auto",
380
+ label: "Auto"
381
+ }, ...(await (await this.ctx.fetchDevice(deviceId)).cameraStreams?.getRtspEntries({}) ?? []).filter((e) => e.enabled).map((e) => e.brokerId.slice(prefix.length)).map((id) => ({
382
+ value: id,
383
+ label: streamQualityLabel(id)
384
+ }))];
385
+ } catch (err) {
386
+ this.ctx.logger.error("getStreamOptions failed", {
387
+ tags: { deviceId },
388
+ meta: { error: errMsg(err) }
389
+ });
390
+ return [{
391
+ value: "auto",
392
+ label: "Auto"
393
+ }];
394
+ }
395
+ }
396
+ async saveDeviceSettingsPatch(deviceId, patch) {
397
+ if (!this.ctx.settings) throw new Error("[snapshot] settings store unavailable cannot persist per-device settings");
398
+ const next = { ...await this.ctx.settings.readDeviceStore(deviceId) };
399
+ if ("snapshotStreamId" in patch) {
400
+ const v = patch["snapshotStreamId"];
401
+ next["snapshotStreamId"] = typeof v === "string" && v.trim().length > 0 ? v.trim() : "";
402
+ }
403
+ if ("snapshotDebug" in patch) next["snapshotDebug"] = patch["snapshotDebug"] === true;
404
+ if ("snapshotMaxAgeS" in patch) {
405
+ const v = patch["snapshotMaxAgeS"];
406
+ if (typeof v === "number" && Number.isFinite(v) && v >= 0) next["snapshotMaxAgeS"] = v;
407
+ else delete next["snapshotMaxAgeS"];
408
+ }
409
+ await this.ctx.settings.writeDeviceStore(deviceId, next);
410
+ this.cache.delete(deviceId);
411
+ return { success: true };
412
+ }
410
413
  };
411
414
  function isAbsentNativeError(msg) {
412
- return msg.includes("no provider for") || msg.includes("no native provider for capability");
415
+ return msg.includes("no provider for") || msg.includes("no native provider for capability");
413
416
  }
417
+ /**
418
+ * Quality ordering for broker picker: `high` > `mid` > `low` > other.
419
+ * The streamId is the suffix after `${deviceId}/` in the brokerId.
420
+ * Unknown labels fall through to 0 so they land at the bottom of the
421
+ * preference list without crashing.
422
+ */
414
423
  function qualityRank(brokerId, prefix) {
415
- const streamId = brokerId.startsWith(prefix) ? brokerId.slice(prefix.length) : brokerId;
416
- const normalized = streamId.toLowerCase();
417
- if (normalized.includes("high") || normalized === "main" || normalized === "hd") return 3;
418
- if (normalized.includes("mid") || normalized === "medium") return 2;
419
- if (normalized.includes("low") || normalized === "sub" || normalized === "sd") return 1;
420
- return 0;
424
+ const normalized = (brokerId.startsWith(prefix) ? brokerId.slice(prefix.length) : brokerId).toLowerCase();
425
+ if (normalized.includes("high") || normalized === "main" || normalized === "hd") return 3;
426
+ if (normalized.includes("mid") || normalized === "medium") return 2;
427
+ if (normalized.includes("low") || normalized === "sub" || normalized === "sd") return 1;
428
+ return 0;
421
429
  }
430
+ /**
431
+ * Detect ffmpeg failure signatures that indicate the broker wasn't
432
+ * streaming yet when we tried to read its RTSP restream — a race
433
+ * window that closes as soon as the broker lands its first keyframe.
434
+ * Used by `grabFrameFromBroker` to retry once instead of giving up.
435
+ */
422
436
  function isBrokerColdError(msg) {
423
- return msg.includes("Invalid data found when processing input") || msg.includes("Error opening input") || msg.includes("Connection refused") || msg.includes("No route to host");
437
+ return msg.includes("Invalid data found when processing input") || msg.includes("Error opening input") || msg.includes("Connection refused") || msg.includes("No route to host");
424
438
  }
425
439
  function runFfmpegFrameGrab(url, timeoutMs) {
426
- return new Promise((resolve, reject) => {
427
- const child = execFile(
428
- "ffmpeg",
429
- [
430
- "-loglevel",
431
- "error",
432
- "-rtsp_transport",
433
- "tcp",
434
- "-fflags",
435
- "+discardcorrupt",
436
- "-skip_frame",
437
- "nokey",
438
- "-i",
439
- url,
440
- "-vf",
441
- "select=eq(pict_type\\,I)",
442
- "-vsync",
443
- "vfr",
444
- "-frames:v",
445
- "1",
446
- "-q:v",
447
- "3",
448
- "-f",
449
- "image2pipe",
450
- "-vcodec",
451
- "mjpeg",
452
- "pipe:1"
453
- ],
454
- { encoding: "buffer", maxBuffer: 16 * 1024 * 1024, timeout: timeoutMs },
455
- (err, stdout, stderr) => {
456
- if (err) {
457
- const errWithMeta = err;
458
- const stderrText = Buffer.isBuffer(stderr) ? stderr.toString("utf8").trim() : String(stderr ?? "").trim();
459
- const parts = [err instanceof Error ? err.message : String(err)];
460
- if (errWithMeta.killed) parts.push("killed=true");
461
- if (errWithMeta.code !== void 0) parts.push(`code=${String(errWithMeta.code)}`);
462
- if (errWithMeta.signal) parts.push(`signal=${errWithMeta.signal}`);
463
- if (stderrText) parts.push(`stderr: ${stderrText.slice(0, 500)}`);
464
- reject(new Error(parts.join(" \u2014 ")));
465
- return;
466
- }
467
- resolve(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
468
- }
469
- );
470
- child.on("error", (e) => reject(e));
471
- });
440
+ return new Promise((resolve, reject) => {
441
+ execFile("ffmpeg", [
442
+ "-loglevel",
443
+ "error",
444
+ "-rtsp_transport",
445
+ "tcp",
446
+ "-fflags",
447
+ "+discardcorrupt",
448
+ "-skip_frame",
449
+ "nokey",
450
+ "-i",
451
+ url,
452
+ "-vf",
453
+ "select=eq(pict_type\\,I)",
454
+ "-vsync",
455
+ "vfr",
456
+ "-frames:v",
457
+ "1",
458
+ "-q:v",
459
+ "3",
460
+ "-f",
461
+ "image2pipe",
462
+ "-vcodec",
463
+ "mjpeg",
464
+ "pipe:1"
465
+ ], {
466
+ encoding: "buffer",
467
+ maxBuffer: 16 * 1024 * 1024,
468
+ timeout: timeoutMs
469
+ }, (err, stdout, stderr) => {
470
+ if (err) {
471
+ const errWithMeta = err;
472
+ const stderrText = Buffer.isBuffer(stderr) ? stderr.toString("utf8").trim() : String(stderr ?? "").trim();
473
+ const parts = [err instanceof Error ? err.message : String(err)];
474
+ if (errWithMeta.killed) parts.push("killed=true");
475
+ if (errWithMeta.code !== void 0) parts.push(`code=${String(errWithMeta.code)}`);
476
+ if (errWithMeta.signal) parts.push(`signal=${errWithMeta.signal}`);
477
+ if (stderrText) parts.push(`stderr: ${stderrText.slice(0, 500)}`);
478
+ reject(new Error(parts.join(" ")));
479
+ return;
480
+ }
481
+ resolve(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout));
482
+ }).on("error", (e) => reject(e));
483
+ });
472
484
  }
473
- export {
474
- SnapshotAddon,
475
- SnapshotAddon as default
476
- };
485
+ //#endregion
486
+ export { SnapshotAddon, SnapshotAddon as default };
487
+
477
488
  //# sourceMappingURL=index.mjs.map