@camstack/system 1.0.2

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 (262) hide show
  1. package/dist/addon/addon-api-factory.d.ts +35 -0
  2. package/dist/addon-routes/addon-route-registry.d.ts +37 -0
  3. package/dist/addon-runner.js +599 -0
  4. package/dist/addon-runner.mjs +597 -0
  5. package/dist/auth/api-key-manager.d.ts +26 -0
  6. package/dist/auth/auth-manager.d.ts +109 -0
  7. package/dist/auth/parse-record.d.ts +18 -0
  8. package/dist/auth/scope-matcher.d.ts +7 -0
  9. package/dist/auth/scoped-token-manager.d.ts +40 -0
  10. package/dist/auth/totp-manager.d.ts +51 -0
  11. package/dist/auth/user-manager.d.ts +34 -0
  12. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.d.ts +53 -0
  13. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +259 -0
  14. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +251 -0
  15. package/dist/builtins/addon-pages-aggregator/dedupe-pages.d.ts +6 -0
  16. package/dist/builtins/addon-pages-aggregator/index.d.ts +1 -0
  17. package/dist/builtins/addon-pages-aggregator/index.js +8 -0
  18. package/dist/builtins/addon-pages-aggregator/index.mjs +2 -0
  19. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.d.ts +47 -0
  20. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +228 -0
  21. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +220 -0
  22. package/dist/builtins/addon-widgets-aggregator/index.d.ts +1 -0
  23. package/dist/builtins/addon-widgets-aggregator/index.js +8 -0
  24. package/dist/builtins/addon-widgets-aggregator/index.mjs +2 -0
  25. package/dist/builtins/alerts/alerts.addon.d.ts +81 -0
  26. package/dist/builtins/alerts/alerts.addon.js +601 -0
  27. package/dist/builtins/alerts/alerts.addon.mjs +595 -0
  28. package/dist/builtins/alerts/index.d.ts +1 -0
  29. package/dist/builtins/alerts/index.js +4 -0
  30. package/dist/builtins/alerts/index.mjs +2 -0
  31. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts +147 -0
  32. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2229 -0
  33. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2220 -0
  34. package/dist/builtins/backup-orchestrator/cron-helpers.d.ts +23 -0
  35. package/dist/builtins/backup-orchestrator/destination-policy.d.ts +72 -0
  36. package/dist/builtins/backup-orchestrator/download-helpers.d.ts +12 -0
  37. package/dist/builtins/backup-orchestrator/index.d.ts +2 -0
  38. package/dist/builtins/backup-orchestrator/index.js +8 -0
  39. package/dist/builtins/backup-orchestrator/index.mjs +2 -0
  40. package/dist/builtins/backup-orchestrator/manifest-store.d.ts +77 -0
  41. package/dist/builtins/console-logging/console-destination.d.ts +13 -0
  42. package/dist/builtins/console-logging/console-logging.addon.d.ts +25 -0
  43. package/dist/builtins/console-logging/index.d.ts +3 -0
  44. package/dist/builtins/console-logging/index.js +104 -0
  45. package/dist/builtins/console-logging/index.mjs +95 -0
  46. package/dist/builtins/device-manager/device-config-contribution.d.ts +32 -0
  47. package/dist/builtins/device-manager/device-event-propagator.d.ts +26 -0
  48. package/dist/builtins/device-manager/device-link-overlay.d.ts +23 -0
  49. package/dist/builtins/device-manager/device-link-resolver.d.ts +15 -0
  50. package/dist/builtins/device-manager/device-manager.addon.d.ts +452 -0
  51. package/dist/builtins/device-manager/device-manager.addon.js +3299 -0
  52. package/dist/builtins/device-manager/device-manager.addon.mjs +3292 -0
  53. package/dist/builtins/device-manager/index.d.ts +2 -0
  54. package/dist/builtins/device-manager/index.js +8 -0
  55. package/dist/builtins/device-manager/index.mjs +2 -0
  56. package/dist/builtins/hub-forwarder/hub-forwarder-destination.d.ts +44 -0
  57. package/dist/builtins/hub-forwarder/hub-forwarder.addon.d.ts +15 -0
  58. package/dist/builtins/hub-forwarder/index.d.ts +3 -0
  59. package/dist/builtins/hub-forwarder/index.js +154 -0
  60. package/dist/builtins/hub-forwarder/index.mjs +145 -0
  61. package/dist/builtins/local-auth/auth-schema.d.ts +26 -0
  62. package/dist/builtins/local-auth/index.d.ts +1 -0
  63. package/dist/builtins/local-auth/index.js +4 -0
  64. package/dist/builtins/local-auth/index.mjs +2 -0
  65. package/dist/builtins/local-auth/local-auth.addon.d.ts +18 -0
  66. package/dist/builtins/local-auth/local-auth.addon.js +8094 -0
  67. package/dist/builtins/local-auth/local-auth.addon.mjs +8063 -0
  68. package/dist/builtins/local-auth/oauth-grants.d.ts +45 -0
  69. package/dist/builtins/local-auth/oauth-session-manager.d.ts +50 -0
  70. package/dist/builtins/local-network/index.d.ts +2 -0
  71. package/dist/builtins/local-network/index.js +10 -0
  72. package/dist/builtins/local-network/index.mjs +2 -0
  73. package/dist/builtins/local-network/local-network.addon.d.ts +150 -0
  74. package/dist/builtins/local-network/local-network.addon.js +489 -0
  75. package/dist/builtins/local-network/local-network.addon.mjs +477 -0
  76. package/dist/builtins/native-metrics/index.d.ts +2 -0
  77. package/dist/builtins/native-metrics/native-metrics-provider.d.ts +48 -0
  78. package/dist/builtins/native-metrics/native-metrics.addon.d.ts +73 -0
  79. package/dist/builtins/native-metrics/native-metrics.addon.js +922 -0
  80. package/dist/builtins/native-metrics/native-metrics.addon.mjs +914 -0
  81. package/dist/builtins/platform-probe/hardware-decode-accel-probe.d.ts +37 -0
  82. package/dist/builtins/platform-probe/hardware-encoder-probe.d.ts +13 -0
  83. package/dist/builtins/platform-probe/index.d.ts +22 -0
  84. package/dist/builtins/platform-probe/index.js +834 -0
  85. package/dist/builtins/platform-probe/index.mjs +822 -0
  86. package/dist/builtins/platform-probe/inference-config-resolver.d.ts +29 -0
  87. package/dist/builtins/platform-probe/intel-accelerators.d.ts +11 -0
  88. package/dist/builtins/platform-probe/platform-scorer.d.ts +30 -0
  89. package/dist/builtins/platform-probe/runtime-packages.d.ts +6 -0
  90. package/dist/builtins/remote-access-orchestrator/enabled-providers-reconcile.d.ts +96 -0
  91. package/dist/builtins/remote-access-orchestrator/index.d.ts +1 -0
  92. package/dist/builtins/remote-access-orchestrator/index.js +8 -0
  93. package/dist/builtins/remote-access-orchestrator/index.mjs +2 -0
  94. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.d.ts +40 -0
  95. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +214 -0
  96. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +208 -0
  97. package/dist/builtins/shared/settle-sources.d.ts +22 -0
  98. package/dist/builtins/snapshot/index.d.ts +2 -0
  99. package/dist/builtins/snapshot/index.js +494 -0
  100. package/dist/builtins/snapshot/index.mjs +488 -0
  101. package/dist/builtins/snapshot/snapshot.addon.d.ts +120 -0
  102. package/dist/builtins/sqlite-storage/config-store.d.ts +8 -0
  103. package/dist/builtins/sqlite-storage/device-store.d.ts +23 -0
  104. package/dist/builtins/sqlite-storage/filesystem-browse-provider.d.ts +25 -0
  105. package/dist/builtins/sqlite-storage/filesystem-storage-provider.d.ts +83 -0
  106. package/dist/builtins/sqlite-storage/filesystem-storage.addon.d.ts +32 -0
  107. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +396 -0
  108. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +388 -0
  109. package/dist/builtins/sqlite-storage/index.d.ts +8 -0
  110. package/dist/builtins/sqlite-storage/index.js +62 -0
  111. package/dist/builtins/sqlite-storage/index.mjs +49 -0
  112. package/dist/builtins/sqlite-storage/integration-registry.d.ts +27 -0
  113. package/dist/builtins/sqlite-storage/path-guard.d.ts +4 -0
  114. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +102 -0
  115. package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +14 -0
  116. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +644 -0
  117. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +636 -0
  118. package/dist/builtins/storage-orchestrator/index.d.ts +6 -0
  119. package/dist/builtins/storage-orchestrator/index.js +10 -0
  120. package/dist/builtins/storage-orchestrator/index.mjs +2 -0
  121. package/dist/builtins/storage-orchestrator/location-store.d.ts +49 -0
  122. package/dist/builtins/storage-orchestrator/provider-discovery.d.ts +10 -0
  123. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +103 -0
  124. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1138 -0
  125. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1128 -0
  126. package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +236 -0
  127. package/dist/builtins/storage-orchestrator/storage-pressure-manager.d.ts +38 -0
  128. package/dist/builtins/system-backup/system-backup.service.d.ts +137 -0
  129. package/dist/builtins/system-config/index.d.ts +1 -0
  130. package/dist/builtins/system-config/index.js +8 -0
  131. package/dist/builtins/system-config/index.mjs +2 -0
  132. package/dist/builtins/system-config/system-config.addon.d.ts +10 -0
  133. package/dist/builtins/system-config/system-config.addon.js +232 -0
  134. package/dist/builtins/system-config/system-config.addon.mjs +226 -0
  135. package/dist/builtins/winston-logging/index.d.ts +3 -0
  136. package/dist/builtins/winston-logging/index.js +156 -0
  137. package/dist/builtins/winston-logging/index.mjs +144 -0
  138. package/dist/builtins/winston-logging/winston-destination.d.ts +21 -0
  139. package/dist/builtins/winston-logging/winston-logging.addon.d.ts +19 -0
  140. package/dist/chunk-CNf5ZN-e.mjs +37 -0
  141. package/dist/chunk-Cek0wNdY.js +64 -0
  142. package/dist/download/model-download-service.d.ts +41 -0
  143. package/dist/download/model-downloader.d.ts +31 -0
  144. package/dist/events/event-bus.d.ts +10 -0
  145. package/dist/events/system-event-bus.d.ts +14 -0
  146. package/dist/feature/feature-manager.d.ts +11 -0
  147. package/dist/formatter-B7qW8bPJ.mjs +162 -0
  148. package/dist/formatter-DqAKDlvN.js +167 -0
  149. package/dist/http/authenticated-file-server.d.ts +53 -0
  150. package/dist/http/data-plane-registry.d.ts +23 -0
  151. package/dist/http/file-data-plane.d.ts +10 -0
  152. package/dist/http/reverse-proxy.d.ts +15 -0
  153. package/dist/index.d.ts +82 -0
  154. package/dist/index.js +93485 -0
  155. package/dist/index.mjs +93179 -0
  156. package/dist/intel-accelerators-Gg0P5mnl.js +20 -0
  157. package/dist/intel-accelerators-hGgpZ0pX.mjs +19 -0
  158. package/dist/kernel/addon-class-resolver.d.ts +4 -0
  159. package/dist/kernel/addon-engine-manager.d.ts +22 -0
  160. package/dist/kernel/addon-health-monitor.d.ts +154 -0
  161. package/dist/kernel/addon-installer.d.ts +208 -0
  162. package/dist/kernel/addon-loader.d.ts +106 -0
  163. package/dist/kernel/addon-manifest.d.ts +77 -0
  164. package/dist/kernel/capability-handle.d.ts +46 -0
  165. package/dist/kernel/capability-registry.d.ts +412 -0
  166. package/dist/kernel/config-manager.d.ts +212 -0
  167. package/dist/kernel/config-schema.d.ts +93 -0
  168. package/dist/kernel/custom-action-registry.d.ts +23 -0
  169. package/dist/kernel/deps/addon-deps-manager.d.ts +19 -0
  170. package/dist/kernel/deps/manifest-native-deps.d.ts +25 -0
  171. package/dist/kernel/deps/manifest-python-deps.d.ts +20 -0
  172. package/dist/kernel/device-registry.d.ts +29 -0
  173. package/dist/kernel/fs-utils.d.ts +41 -0
  174. package/dist/kernel/hwaccel/hwaccel-resolver.d.ts +19 -0
  175. package/dist/kernel/hwaccel/hwaccel-service.d.ts +4 -0
  176. package/dist/kernel/index.d.ts +74 -0
  177. package/dist/kernel/infra-capabilities.d.ts +13 -0
  178. package/dist/kernel/moleculer/addon-context-factory.d.ts +91 -0
  179. package/dist/kernel/moleculer/addon-data-plane-facility.d.ts +19 -0
  180. package/dist/kernel/moleculer/addon-runner.d.ts +1 -0
  181. package/dist/kernel/moleculer/addon-service-factory.d.ts +50 -0
  182. package/dist/kernel/moleculer/broker-factory.d.ts +50 -0
  183. package/dist/kernel/moleculer/cap-usage-registry.d.ts +46 -0
  184. package/dist/kernel/moleculer/capabilities-access.d.ts +21 -0
  185. package/dist/kernel/moleculer/child-addon-call-dispatch.d.ts +46 -0
  186. package/dist/kernel/moleculer/child-cap-dispatch.d.ts +20 -0
  187. package/dist/kernel/moleculer/cluster-secret.d.ts +15 -0
  188. package/dist/kernel/moleculer/core-cap-service.d.ts +50 -0
  189. package/dist/kernel/moleculer/crash-supervisor.d.ts +50 -0
  190. package/dist/kernel/moleculer/device-cap-proxy.d.ts +79 -0
  191. package/dist/kernel/moleculer/event-bus-core.d.ts +53 -0
  192. package/dist/kernel/moleculer/event-bus.d.ts +53 -0
  193. package/dist/kernel/moleculer/hub-log-forwarder.d.ts +36 -0
  194. package/dist/kernel/moleculer/hub-service.d.ts +35 -0
  195. package/dist/kernel/moleculer/node-registry.d.ts +126 -0
  196. package/dist/kernel/moleculer/process-context.d.ts +4 -0
  197. package/dist/kernel/moleculer/process-service.d.ts +72 -0
  198. package/dist/kernel/moleculer/provider-registry.d.ts +28 -0
  199. package/dist/kernel/moleculer/readiness-context.d.ts +62 -0
  200. package/dist/kernel/moleculer/readiness-service.d.ts +7 -0
  201. package/dist/kernel/moleculer/register-node-client.d.ts +35 -0
  202. package/dist/kernel/moleculer/remote-logger.d.ts +43 -0
  203. package/dist/kernel/moleculer/resilient-cap-call.d.ts +28 -0
  204. package/dist/kernel/moleculer/stream-probe-service.d.ts +9 -0
  205. package/dist/kernel/moleculer/trpc-links.d.ts +189 -0
  206. package/dist/kernel/moleculer/typed-array-serde.d.ts +25 -0
  207. package/dist/kernel/moleculer/worker-device-restore.d.ts +10 -0
  208. package/dist/kernel/provider-kind-drift.d.ts +12 -0
  209. package/dist/kernel/restart-coordinator.d.ts +90 -0
  210. package/dist/kernel/storage-location-registry.d.ts +40 -0
  211. package/dist/kernel/transport/cap-action-name.d.ts +100 -0
  212. package/dist/kernel/transport/cap-route-resolver.d.ts +148 -0
  213. package/dist/kernel/transport/cap-route.d.ts +148 -0
  214. package/dist/kernel/transport/child-cap-protocol.d.ts +136 -0
  215. package/dist/kernel/transport/create-local-transport.d.ts +7 -0
  216. package/dist/kernel/transport/frame-codec.d.ts +7 -0
  217. package/dist/kernel/transport/index.d.ts +27 -0
  218. package/dist/kernel/transport/local-child-client.d.ts +136 -0
  219. package/dist/kernel/transport/local-child-registry.d.ts +179 -0
  220. package/dist/kernel/transport/local-endpoint-path.d.ts +6 -0
  221. package/dist/kernel/transport/local-transport.d.ts +46 -0
  222. package/dist/kernel/transport/parent-unowned-call.d.ts +75 -0
  223. package/dist/kernel/transport/socket-channel.d.ts +27 -0
  224. package/dist/kernel/transport/uds-event-bridge.d.ts +36 -0
  225. package/dist/kernel/transport/uds-event-bus.d.ts +22 -0
  226. package/dist/kernel/transport/uds-local-transport.d.ts +18 -0
  227. package/dist/kernel/transport/uds-log-ingest.d.ts +28 -0
  228. package/dist/kernel/transport/uds-logger.d.ts +44 -0
  229. package/dist/kernel/utils/ring-buffer.d.ts +15 -0
  230. package/dist/kernel/workspace-detect.d.ts +9 -0
  231. package/dist/lifecycle/lifecycle-state-machine.d.ts +28 -0
  232. package/dist/logging/formatter.d.ts +30 -0
  233. package/dist/logging/log-manager.d.ts +54 -0
  234. package/dist/logging/log-ring-buffer.d.ts +47 -0
  235. package/dist/logging/partitioned-log-buffer.d.ts +35 -0
  236. package/dist/logging/scoped-logger.d.ts +17 -0
  237. package/dist/main-DNnMW7Z2.js +9983 -0
  238. package/dist/main-rtjOwPBR.mjs +9976 -0
  239. package/dist/manifest-python-deps-D1DbAQEv.js +6724 -0
  240. package/dist/manifest-python-deps-DZsKTbs1.mjs +6315 -0
  241. package/dist/network/network-quality.d.ts +11 -0
  242. package/dist/notification/notification-service.d.ts +37 -0
  243. package/dist/notification/toast-service.d.ts +22 -0
  244. package/dist/pipeline/engine-manager-resolver.d.ts +15 -0
  245. package/dist/pipeline/pipeline-runner.d.ts +8 -0
  246. package/dist/pipeline/pipeline-validator.d.ts +13 -0
  247. package/dist/process/resource-monitor.d.ts +11 -0
  248. package/dist/python/python-env-manager.d.ts +12 -0
  249. package/dist/repl/interfaces.d.ts +31 -0
  250. package/dist/repl/repl-engine.d.ts +8 -0
  251. package/dist/resource-monitor-ClDGFyf6.mjs +57 -0
  252. package/dist/resource-monitor-IIEanuJt.js +74 -0
  253. package/dist/settle-sources-Bhsy57y-.js +38 -0
  254. package/dist/settle-sources-CDtNC8ub.mjs +33 -0
  255. package/dist/storage/fs-storage-backend.d.ts +40 -0
  256. package/dist/storage/storage-location-manager.d.ts +23 -0
  257. package/dist/storage/storage-manager.d.ts +83 -0
  258. package/dist/tar-BgAEMRBR.js +5434 -0
  259. package/dist/tar-ByMOPNM0.mjs +5429 -0
  260. package/dist/tls/cert-manager.d.ts +26 -0
  261. package/dist/tls/index.d.ts +1 -0
  262. package/package.json +343 -0
@@ -0,0 +1,597 @@
1
+ import { $ as setWorkerNativeCapsChangeListener, A as createUdsLoggerWithControl, L as LocalChildClient, X as getWorkerNativeCapProvider, Z as getWorkerNativeCapSnapshot, dt as CustomActionRegistry, i as createUdsAddonContext, mt as resolveAddonClass, t as installManifestPythonDeps, tt as validateProviderRegistrations } from "./manifest-python-deps-DZsKTbs1.mjs";
2
+ import * as fs from "node:fs";
3
+ import * as path$1 from "node:path";
4
+ import { ReadinessRegistry, ReadinessTimeoutError, errMsg, normalizeAddonInitResult } from "@camstack/types";
5
+ //#region src/kernel/moleculer/worker-device-restore.ts
6
+ /**
7
+ * Worker-side device restore helper.
8
+ *
9
+ * Shared by `process-runner` (single-addon subprocess) and
10
+ * `group-runner` (group subprocess). Calls the worker addon's
11
+ * `restoreDevices(SavedDevice[])` with devices read from the hub's
12
+ * `device-manager` capability. Blocks on the `system.ready-state`
13
+ * readiness of the hub's `device-manager` via
14
+ * `ReadinessRegistry.awaitReady`, then issues the calls ONCE.
15
+ *
16
+ * Lives in its own module so it can be imported without triggering
17
+ * the runner entry-point side effects (each runner is also an
18
+ * executable that calls `main()` at import time).
19
+ */
20
+ async function runWorkerDeviceRestoreWithRetry(addon, context, addonId, sourceNodeId) {
21
+ const log = context.logger;
22
+ log?.debug?.(`[worker-restore] entry: addon="${addonId}" sourceNodeId="${sourceNodeId}"`);
23
+ const restoreFn = addon.restoreDevices;
24
+ if (typeof restoreFn !== "function") {
25
+ log?.warn?.(`[worker-restore] "${addonId}": no restoreDevices function — skipping`);
26
+ return;
27
+ }
28
+ const api = context.api;
29
+ if (!api) {
30
+ log?.warn?.(`[worker-restore] "${addonId}": context.api missing — skipping`);
31
+ return;
32
+ }
33
+ const bus = context.eventBus;
34
+ if (!bus) {
35
+ log?.warn?.(`[worker-restore] "${addonId}": context.eventBus missing — skipping`);
36
+ return;
37
+ }
38
+ const shared = context.kernel?.readinessRegistry ?? null;
39
+ const registry = shared ?? new ReadinessRegistry({
40
+ eventBus: bus,
41
+ sourceNodeId,
42
+ logger: context.logger
43
+ });
44
+ log?.debug?.(`[worker-restore] "${addonId}": awaiting device-manager readiness on hub...`);
45
+ try {
46
+ await registry.awaitReady("device-manager", {
47
+ type: "node",
48
+ nodeId: "hub"
49
+ }, { timeoutMs: 6e4 });
50
+ log?.debug?.(`[worker-restore] "${addonId}": device-manager READY`);
51
+ } catch (err) {
52
+ if (err instanceof ReadinessTimeoutError) {
53
+ context.logger?.warn?.(`[worker-restore] device-manager not ready within ${err.waitedMs}ms — skipping for "${addonId}"`);
54
+ return;
55
+ }
56
+ throw err;
57
+ } finally {
58
+ if (!shared && registry instanceof ReadinessRegistry) registry.close();
59
+ }
60
+ try {
61
+ const deviceManager = Reflect.get(api, "deviceManager");
62
+ log?.debug?.(`[worker-restore] "${addonId}": calling listPersistedByAddon...`);
63
+ const rowsResult = await deviceManager.listPersistedByAddon.query({ addonId });
64
+ log?.debug?.(`[worker-restore] "${addonId}": got ${Array.isArray(rowsResult) ? rowsResult.length : 0} row(s)`);
65
+ const rows = Array.isArray(rowsResult) ? rowsResult : [];
66
+ if (rows.length === 0) {
67
+ context.logger?.info?.(`[worker-restore] no persisted devices to restore for addon "${addonId}"`);
68
+ return;
69
+ }
70
+ const savedDevices = await Promise.all(rows.map(async (row) => {
71
+ const config = await deviceManager.loadConfig.query({ deviceId: row.id });
72
+ return {
73
+ id: row.id,
74
+ stableId: row.stableId,
75
+ type: row.type,
76
+ name: row.name,
77
+ parentDeviceId: row.parentDeviceId,
78
+ config: config ?? {}
79
+ };
80
+ }));
81
+ await restoreFn.call(addon, savedDevices);
82
+ log?.info?.(`[worker-restore] "${addonId}": restored ${savedDevices.length} device(s)`);
83
+ } catch (err) {
84
+ log?.warn?.(`[worker-restore] "${addonId}": restoreDevices threw: ${errMsg(err)}`);
85
+ }
86
+ }
87
+ //#endregion
88
+ //#region src/kernel/moleculer/child-cap-dispatch.ts
89
+ /** Type guard narrowing a dynamically-read provider member to a callable method. */
90
+ function isCapMethod(value) {
91
+ return typeof value === "function";
92
+ }
93
+ /**
94
+ * Creates a unified dispatcher that routes a {@link CapCallInput} to either a
95
+ * device-scoped or singleton provider, then invokes the requested method.
96
+ *
97
+ * Pure module — no Moleculer, no broker, no addon-runner dependency.
98
+ */
99
+ function createChildCapDispatch(deps) {
100
+ return async (call) => {
101
+ const { capName, method, args, deviceId } = call;
102
+ let provider;
103
+ if (deviceId !== void 0) {
104
+ provider = deps.getDeviceProvider(capName, deviceId) ?? deps.getSingletonProvider(capName);
105
+ if (provider == null) throw new Error(`child-cap-dispatch: no provider for '${capName}' (device '${String(deviceId)}' or singleton)`);
106
+ } else {
107
+ provider = deps.getSingletonProvider(capName);
108
+ if (provider == null) throw new Error(`child-cap-dispatch: no singleton provider for '${capName}'`);
109
+ }
110
+ const fn = Reflect.get(provider, method);
111
+ if (!isCapMethod(fn)) throw new Error(`child-cap-dispatch: method '${method}' not found on provider '${capName}'`);
112
+ return await fn.call(provider, args);
113
+ };
114
+ }
115
+ //#endregion
116
+ //#region src/kernel/moleculer/child-addon-call-dispatch.ts
117
+ function isRecord$1(value) {
118
+ return typeof value === "object" && value !== null && !Array.isArray(value);
119
+ }
120
+ /**
121
+ * Strip the (non-serializable) `handler` from a route descriptor, keeping only
122
+ * the keys the hub needs to mount + describe the route. The hub bridges actual
123
+ * dispatch through the `addon-routes` cap's `invoke` method, so the handler
124
+ * never needs to cross the wire.
125
+ */
126
+ function toSerializableRoute(route) {
127
+ if (!isRecord$1(route)) throw new Error("child-addon-call: getRoutes() returned a non-object route descriptor");
128
+ const method = route["method"];
129
+ const path = route["path"];
130
+ if (typeof method !== "string" || typeof path !== "string") throw new Error("child-addon-call: route descriptor missing method/path");
131
+ const access = route["access"];
132
+ const description = route["description"];
133
+ return {
134
+ method,
135
+ path,
136
+ ...typeof access === "string" ? { access } : {},
137
+ ...typeof description === "string" ? { description } : {}
138
+ };
139
+ }
140
+ /**
141
+ * Dispatch a `target: 'settings'` addon-call to the loaded addon instance.
142
+ * Mirrors the (removed) `<addonId>.settings.<method>` Moleculer action. The
143
+ * inner `method` switch is also exhaustive (`never` check) so a new settings
144
+ * method added to `AddonSettingsMethod` must be handled here too. Returns the
145
+ * raw method result; the hub reshapes get-results and acks updates.
146
+ */
147
+ async function dispatchSettings(addon, addonId, method, args) {
148
+ switch (method) {
149
+ case "getGlobalSettings": return typeof addon.getGlobalSettings === "function" ? await addon.getGlobalSettings(args.overlay, args.cap) ?? null : null;
150
+ case "updateGlobalSettings":
151
+ if (typeof addon.updateGlobalSettings !== "function") throw new Error(`child-addon-call: addon "${addonId}" does not implement updateGlobalSettings`);
152
+ await addon.updateGlobalSettings(args.patch ?? {});
153
+ return { success: true };
154
+ case "getDeviceSettings": return typeof addon.getDeviceSettings === "function" && typeof args.deviceId === "number" ? await addon.getDeviceSettings(args.deviceId) ?? null : null;
155
+ case "updateDeviceSettings":
156
+ if (typeof addon.updateDeviceSettings !== "function") throw new Error(`child-addon-call: addon "${addonId}" does not implement updateDeviceSettings`);
157
+ if (typeof args.deviceId !== "number") throw new Error(`child-addon-call: addon "${addonId}" updateDeviceSettings missing deviceId`);
158
+ await addon.updateDeviceSettings(args.deviceId, args.patch ?? {});
159
+ return { success: true };
160
+ default: throw new Error(`child-addon-call: unhandled settings method "${String(method)}"`);
161
+ }
162
+ }
163
+ /**
164
+ * Creates the addon-level dispatcher the addon-runner wires to
165
+ * `LocalChildClient.onAddonCall`. Routes a {@link AddonCallInput} to one of the
166
+ * exhaustive {@link AddonCallTarget} surfaces (routes / custom / settings) —
167
+ * everything the per-addon Moleculer broker used to carry before the UDS
168
+ * migration removed it. The `target` switch is exhaustive (`never` check): a
169
+ * new surface added to the protocol union that isn't dispatched here is a
170
+ * COMPILE ERROR — the guard that makes a "missed link" impossible.
171
+ *
172
+ * Pure module — no Moleculer, no broker, no addon-runner dependency.
173
+ */
174
+ function createChildAddonCallDispatch(deps) {
175
+ return async (call) => {
176
+ const { addonId, target } = call;
177
+ switch (target) {
178
+ case "routes": {
179
+ const provider = deps.getRouteProvider(addonId);
180
+ if (provider == null) throw new Error(`child-addon-call: addon "${addonId}" has no addon-routes provider`);
181
+ const getRoutes = Reflect.get(provider, "getRoutes");
182
+ if (typeof getRoutes !== "function") throw new Error(`child-addon-call: addon "${addonId}" addon-routes provider has no getRoutes()`);
183
+ const live = await getRoutes.call(provider);
184
+ if (!Array.isArray(live)) throw new Error(`child-addon-call: addon "${addonId}" getRoutes() did not return an array`);
185
+ return live.map(toSerializableRoute);
186
+ }
187
+ case "custom": {
188
+ const action = call.action;
189
+ if (typeof action !== "string" || action.length === 0) throw new Error(`child-addon-call: addon "${addonId}" custom call missing action name`);
190
+ const entry = deps.customActions.resolve(addonId, action);
191
+ if (entry === null) throw new Error(`child-addon-call: addon "${addonId}" has no custom action "${action}"`);
192
+ return entry.handler(call.args);
193
+ }
194
+ case "settings": {
195
+ const method = call.method;
196
+ if (method == null) throw new Error(`child-addon-call: addon "${addonId}" settings call missing method`);
197
+ const addon = deps.getAddonInstance(addonId);
198
+ if (addon == null) throw new Error(`child-addon-call: addon "${addonId}" not loaded on this node`);
199
+ return dispatchSettings(addon, addonId, method, call.args ?? {});
200
+ }
201
+ case "data-planes": return deps.getDataPlanes(addonId);
202
+ default: throw new Error(`child-addon-call: unhandled addon-call target "${String(target)}"`);
203
+ }
204
+ };
205
+ }
206
+ //#endregion
207
+ //#region src/kernel/moleculer/addon-runner.ts
208
+ /**
209
+ * Addon Runner — THE unified entry point for spawned subprocesses.
210
+ *
211
+ * Every spawned process is a size-1 addon set — one addon, one process,
212
+ * zero exceptions (D2/D9). The media-pipeline co-location group was
213
+ * dissolved by Phase 5 once frames and audio chunks became serialisable
214
+ * (shm `FrameHandle`s / tRPC), so no addon declares `execution.group`
215
+ * and `CAMSTACK_RUNNER_ADDONS` always carries a single element. The
216
+ * runner still accepts a multi-element array structurally — that is a
217
+ * mechanism, not a policy; no manifest exercises it.
218
+ *
219
+ * UDS-ONLY (Phase F1+F2 — irreversible). The forked child runs with NO
220
+ * Moleculer `ServiceBroker`. Its only link to the parent (hub or agent)
221
+ * is a `LocalChildClient` over a Unix-domain socket:
222
+ *
223
+ * - Caps: `ctx.api.<cap>.<method>(...)` resolves a same-process
224
+ * sibling via `localProviderLink`; everything else rides
225
+ * `ipcParentLink → client.callOut` → the parent's
226
+ * `onUnownedCall` (resolver-first + broker-fallback) — F0.
227
+ * - Events: `createUdsEventBus` — local `deliverShared` + UDS forward.
228
+ * - Logs: `createUdsLogger` — `client.sendLog` (pre-connect buffered).
229
+ * - Readiness: `getOrInitReadinessRegistryForClient` — hydrate via
230
+ * `client.requestReadinessSnapshot()` on connect.
231
+ * - Registration: the UDS `register`/`updateCaps` message IS the
232
+ * handshake (the parent applies it via `onChildRegistered`,
233
+ * mirroring `$hub.registerNode`). No Moleculer RPC.
234
+ *
235
+ * CRITICAL ORDERING: `udsChildClient.start()` MUST resolve BEFORE any addon
236
+ * module is loaded/initialised — addon `initialize()` issues `ctx.api` /
237
+ * `ctx.settings` calls that route over UDS via `ipcParentLink`, and there is
238
+ * no broker fallback anymore. The client's pre-connect buffers flush on
239
+ * connect, so init-time logs/events are preserved.
240
+ *
241
+ * Environment variables:
242
+ * - CAMSTACK_RUNNER_ID — runner label (becomes the nodeID suffix)
243
+ * - CAMSTACK_RUNNER_ADDONS — JSON array of `{ addonId, addonDir }`
244
+ * - CAMSTACK_PARENT_NODE_ID — hub or agent nodeID
245
+ * - CAMSTACK_PARENT_UDS_PATH — REQUIRED: the parent's UDS endpoint
246
+ * - CAMSTACK_DATA_DIR — addon data root (default ./camstack-data)
247
+ * - CAMSTACK_LOG_LEVEL — log level (default debug)
248
+ *
249
+ * Exits non-zero on fatal init failures so the parent process supervisor
250
+ * can respawn. Graceful SIGTERM/SIGINT triggers `addon.shutdown()` for
251
+ * every loaded addon before closing the UDS channel.
252
+ */
253
+ process.on("uncaughtException", (err) => {
254
+ console.error(`[addon-runner CRASH] uncaughtException: ${errMsg(err)}\n${err instanceof Error ? err.stack ?? "" : ""}`);
255
+ process.exit(1);
256
+ });
257
+ process.on("unhandledRejection", (reason) => {
258
+ console.error(`[addon-runner CRASH] unhandledRejection: ${errMsg(reason)}\n${reason instanceof Error ? reason.stack ?? "" : ""}`);
259
+ });
260
+ process.on("exit", (code) => {
261
+ console.error(`[addon-runner EXIT] code=${String(code)} nodeId=${process.env["CAMSTACK_RUNNER_ID"] ?? "?"}`);
262
+ });
263
+ for (const sig of [
264
+ "SIGTERM",
265
+ "SIGINT",
266
+ "SIGHUP"
267
+ ]) process.on(sig, () => {
268
+ console.error(`[addon-runner SIGNAL] received ${sig} nodeId=${process.env["CAMSTACK_RUNNER_ID"] ?? "?"}`);
269
+ });
270
+ function isRecord(value) {
271
+ return typeof value === "object" && value !== null && !Array.isArray(value);
272
+ }
273
+ function parseAddonsSpec(raw) {
274
+ if (!raw) return [];
275
+ let parsed;
276
+ try {
277
+ parsed = JSON.parse(raw);
278
+ } catch (err) {
279
+ throw new Error(`[addon-runner] CAMSTACK_RUNNER_ADDONS is not valid JSON: ${errMsg(err)}`, { cause: err });
280
+ }
281
+ if (!Array.isArray(parsed)) throw new Error(`[addon-runner] CAMSTACK_RUNNER_ADDONS must be a JSON array`);
282
+ const specs = [];
283
+ for (const entry of parsed) {
284
+ if (!isRecord(entry)) throw new Error(`[addon-runner] addon spec is not an object: ${JSON.stringify(entry)}`);
285
+ const addonId = entry["addonId"];
286
+ const addonDir = entry["addonDir"];
287
+ if (typeof addonId !== "string" || typeof addonDir !== "string") throw new Error(`[addon-runner] addon spec missing addonId/addonDir: ${JSON.stringify(entry)}`);
288
+ specs.push({
289
+ addonId,
290
+ addonDir
291
+ });
292
+ }
293
+ return specs;
294
+ }
295
+ async function loadAddonModule(addonId, addonDir) {
296
+ const pkgJsonPath = path$1.join(addonDir, "package.json");
297
+ if (!fs.existsSync(pkgJsonPath)) throw new Error(`No package.json at ${addonDir}`);
298
+ const declaration = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"))["camstack"]?.addons?.find((a) => a.id === addonId);
299
+ if (!declaration?.entry) throw new Error(`Addon "${addonId}" not found in ${pkgJsonPath} manifest`);
300
+ const entryPath = path$1.resolve(addonDir, declaration.entry);
301
+ if (!fs.existsSync(entryPath)) throw new Error(`Entry point not found: ${entryPath}`);
302
+ const modUnknown = await import(entryPath);
303
+ const mod = isRecord(modUnknown) ? modUnknown : {};
304
+ const AddonClass = resolveAddonClass(mod);
305
+ if (!AddonClass) throw new Error(`No addon class exported from ${entryPath}`);
306
+ return {
307
+ declaration,
308
+ mod,
309
+ AddonClass
310
+ };
311
+ }
312
+ /**
313
+ * Builds the {@link ChildCapDescriptor} array that the UDS child transport
314
+ * sends to the parent on registration.
315
+ *
316
+ * - Singleton caps come from the runner UNION manifest (all caps registered by
317
+ * every addon in this runner). One descriptor per unique cap name, mode
318
+ * `'singleton'` and no `deviceId`.
319
+ * - Device-scoped caps come from the native-cap snapshot — one descriptor per
320
+ * (capName, deviceId) pair, mode `'collection'`.
321
+ */
322
+ function buildChildCapDescriptors(manifest, nativeCapSnapshot) {
323
+ const descriptors = [];
324
+ const seen = /* @__PURE__ */ new Set();
325
+ for (const entry of manifest) for (const capName of entry.capabilities) if (!seen.has(capName)) {
326
+ seen.add(capName);
327
+ descriptors.push({
328
+ capName,
329
+ mode: "singleton"
330
+ });
331
+ }
332
+ for (const { capName, deviceIds } of nativeCapSnapshot) for (const deviceId of deviceIds) descriptors.push({
333
+ capName,
334
+ mode: "collection",
335
+ deviceId
336
+ });
337
+ return descriptors;
338
+ }
339
+ async function main() {
340
+ const runnerId = process.env["CAMSTACK_RUNNER_ID"];
341
+ const addonsJson = process.env["CAMSTACK_RUNNER_ADDONS"];
342
+ const parentNodeId = process.env["CAMSTACK_PARENT_NODE_ID"];
343
+ const dataDir = process.env["CAMSTACK_DATA_DIR"] ?? "./camstack-data";
344
+ if (!runnerId || !addonsJson || !parentNodeId) {
345
+ console.error("[addon-runner] Missing required env vars: CAMSTACK_RUNNER_ID, CAMSTACK_RUNNER_ADDONS, CAMSTACK_PARENT_NODE_ID");
346
+ process.exit(1);
347
+ }
348
+ let specs;
349
+ try {
350
+ specs = parseAddonsSpec(addonsJson);
351
+ } catch (err) {
352
+ console.error(errMsg(err));
353
+ process.exit(1);
354
+ }
355
+ if (specs.length === 0) {
356
+ console.error("[addon-runner] CAMSTACK_RUNNER_ADDONS resolved to 0 addons — refusing to start");
357
+ process.exit(1);
358
+ }
359
+ const nodeId = `${parentNodeId}/${runnerId}`;
360
+ if (!process.env["CAMSTACK_PARENT_UDS_PATH"]) {
361
+ console.error(`[addon-runner] "${nodeId}" CAMSTACK_PARENT_UDS_PATH is unset — broker-less runner cannot reach its parent. Exiting.`);
362
+ process.exit(1);
363
+ }
364
+ console.log(`[addon-runner] Starting "${nodeId}" (UDS-only) with ${specs.length} addon(s): ${specs.map((s) => s.addonId).join(", ")}`);
365
+ const loadedModules = [];
366
+ for (const spec of specs) try {
367
+ const { declaration, AddonClass } = await loadAddonModule(spec.addonId, spec.addonDir);
368
+ loadedModules.push({
369
+ spec,
370
+ declaration,
371
+ AddonClass
372
+ });
373
+ } catch (err) {
374
+ console.error(`[addon-runner] Failed to load "${spec.addonId}": ${errMsg(err)}`);
375
+ process.exit(1);
376
+ }
377
+ let storageProvider;
378
+ let storageProviderError;
379
+ try {
380
+ const { FilesystemStorageProvider } = await import("@camstack/types/node");
381
+ storageProvider = new FilesystemStorageProvider(dataDir);
382
+ } catch (err) {
383
+ storageProviderError = err;
384
+ }
385
+ const INFRA_CAP_NAMES = new Set([
386
+ "settings-store",
387
+ "storage",
388
+ "log-destination",
389
+ "metrics-provider",
390
+ "platform-probe"
391
+ ]);
392
+ const infraPriority = (decl) => {
393
+ const caps = decl.capabilities ?? [];
394
+ for (const cap of caps) {
395
+ const name = typeof cap === "string" ? cap : cap.name;
396
+ if (INFRA_CAP_NAMES.has(name)) return 0;
397
+ }
398
+ return 1;
399
+ };
400
+ loadedModules.sort((a, b) => infraPriority(a.declaration) - infraPriority(b.declaration));
401
+ /** UNION manifest accumulates one entry per addon as each initialises. */
402
+ const runnerManifest = [];
403
+ /**
404
+ * Aggregated singleton (system-scoped) providers across ALL addons in this
405
+ * runner — keyed by capName. Read by the UDS child-cap dispatch to resolve
406
+ * singleton calls (no Moleculer). Populated during the addon-init loop; a
407
+ * cap-call that arrives before its addon initialises is rejected by the
408
+ * dispatcher (the parent only routes caps the child has registered via the
409
+ * UDS `register`/`updateCaps` handshake).
410
+ */
411
+ const allSingletonProviders = /* @__PURE__ */ new Map();
412
+ /**
413
+ * Per-addon route-provider map — keyed by addonId → the addon's live
414
+ * `addon-routes` provider object. Resolves `getRouteProvider(addonId)`
415
+ * per-addon rather than returning a single global provider. Populated
416
+ * during the init loop alongside `allSingletonProviders`.
417
+ *
418
+ * This is the correct abstraction even in the current single-addon-per-runner
419
+ * policy (D2/D9): it makes the lookup honest and future-safe for any explicit
420
+ * co-location scenario where two addons each declare `addon-routes`.
421
+ */
422
+ const addonRouteProviders = /* @__PURE__ */ new Map();
423
+ /**
424
+ * Per-addon HTTP data-plane endpoints — keyed by addonId → the descriptors
425
+ * the addon's `ctx.dataPlane` facility published (via the `dataPlaneSink`
426
+ * passed into the context). Read by the UDS `target:'data-planes'` dispatch
427
+ * so the hub can pull them and set up its reverse-proxy. Mirrors
428
+ * `addonRouteProviders`.
429
+ */
430
+ const addonDataPlanes = /* @__PURE__ */ new Map();
431
+ /**
432
+ * Per-addon instance map — keyed by addonId → the loaded addon instance.
433
+ * Backs the `settings` addon-call surface (`getAddonInstance`), which invokes
434
+ * the addon's global/device settings methods. Populated during the init loop;
435
+ * read lazily at call time (the dispatcher only fires after init completes).
436
+ */
437
+ const loadedAddonInstances = /* @__PURE__ */ new Map();
438
+ /**
439
+ * Per-addon custom-action registry — populated during the init loop. Read by
440
+ * the UDS addon-call dispatch to resolve `target:'custom'` calls (mirrors the
441
+ * removed `custom.<action>` Moleculer action). Constructed up front so the
442
+ * addon-call handler can close over it at client-construction time.
443
+ */
444
+ const customActionRegistry = new CustomActionRegistry();
445
+ let shuttingDown = false;
446
+ const udsChildClient = new LocalChildClient({
447
+ nodeId: parentNodeId,
448
+ childId: runnerId,
449
+ caps: buildChildCapDescriptors(runnerManifest, getWorkerNativeCapSnapshot()),
450
+ dispatch: createChildCapDispatch({
451
+ getSingletonProvider: (capName) => allSingletonProviders.get(capName) ?? null,
452
+ getDeviceProvider: (capName, deviceId) => getWorkerNativeCapProvider(capName, deviceId) ?? null
453
+ })
454
+ });
455
+ udsChildClient.onAddonCall(createChildAddonCallDispatch({
456
+ getRouteProvider: (addonId) => addonRouteProviders.get(addonId) ?? null,
457
+ customActions: customActionRegistry,
458
+ getAddonInstance: (addonId) => loadedAddonInstances.get(addonId) ?? null,
459
+ getDataPlanes: (addonId) => addonDataPlanes.get(addonId) ?? []
460
+ }));
461
+ const { logger: runnerLog, control: runnerLogControl } = createUdsLoggerWithControl(udsChildClient, runnerId, nodeId, "addon-runner");
462
+ udsChildClient.onSetLogLevel((level) => {
463
+ runnerLogControl.setLevel(level);
464
+ runnerLog.info(`Log level changed to "${level}" via UDS`);
465
+ });
466
+ try {
467
+ await udsChildClient.start();
468
+ } catch (err) {
469
+ console.error(`[addon-runner] "${nodeId}" UDS connect failed — exiting: ${errMsg(err)}`);
470
+ process.exit(1);
471
+ }
472
+ runnerLog.info("UDS child transport connected", { meta: { childId: runnerId } });
473
+ if (storageProvider) runnerLog.info(`"${nodeId}" storage provider rooted at ${dataDir}`);
474
+ else runnerLog.warn(`"${nodeId}" failed to load FilesystemStorageProvider`, { meta: { error: errMsg(storageProviderError) } });
475
+ const refreshChildCaps = (reason) => {
476
+ const freshDescriptors = buildChildCapDescriptors(runnerManifest, getWorkerNativeCapSnapshot());
477
+ udsChildClient.updateCaps(freshDescriptors).catch((err) => {
478
+ runnerLog.warn(`UDS updateCaps failed (${reason})`, { meta: { error: err instanceof Error ? err.message : String(err) } });
479
+ });
480
+ };
481
+ let capRefreshTimer;
482
+ setWorkerNativeCapsChangeListener(() => {
483
+ if (capRefreshTimer !== void 0) return;
484
+ capRefreshTimer = setTimeout(() => {
485
+ capRefreshTimer = void 0;
486
+ refreshChildCaps("native-cap-change");
487
+ }, 200);
488
+ capRefreshTimer.unref?.();
489
+ });
490
+ const loaded = [];
491
+ const deviceRestorePromises = [];
492
+ for (const { spec, declaration, AddonClass } of loadedModules) try {
493
+ const addon = new AddonClass();
494
+ const context = await createUdsAddonContext(udsChildClient, nodeId, declaration, dataDir, {
495
+ ...storageProvider ? { storageProvider } : {},
496
+ dataPlaneSink: { set: (addonId, endpoints) => {
497
+ addonDataPlanes.set(addonId, endpoints);
498
+ } }
499
+ });
500
+ await installManifestPythonDeps(declaration, spec.addonDir, context.deps, context.logger);
501
+ const registeredProviders = /* @__PURE__ */ new Map();
502
+ const initResult = normalizeAddonInitResult(await addon.initialize(context));
503
+ for (const reg of initResult?.providers ?? []) {
504
+ const capName = reg.capability.name;
505
+ registeredProviders.set(capName, reg.provider);
506
+ context.registerProvider(capName, reg.provider);
507
+ const prov = reg.provider;
508
+ if (prov !== null && typeof prov === "object" && !Array.isArray(prov)) {
509
+ allSingletonProviders.set(capName, prov);
510
+ if (capName === "addon-routes") addonRouteProviders.set(spec.addonId, prov);
511
+ }
512
+ }
513
+ if (initResult?.customActions && initResult.actionHandlers) {
514
+ const handlers = initResult.actionHandlers;
515
+ customActionRegistry.registerAddon(spec.addonId, initResult.customActions, (action, input) => {
516
+ const fn = handlers[action];
517
+ if (!fn) throw new Error(`addon '${spec.addonId}' has no handler for custom action '${action}'`);
518
+ return fn(input);
519
+ });
520
+ }
521
+ validateProviderRegistrations(spec.addonId, declaration, registeredProviders, context.logger);
522
+ const capNames = [...registeredProviders.keys()];
523
+ context.logger.info(`"${spec.addonId}" caps: ${capNames.length > 0 ? capNames.join(", ") : "(none)"}`);
524
+ runnerManifest.push({
525
+ addonId: spec.addonId,
526
+ capabilities: capNames
527
+ });
528
+ if (typeof addon.restoreDevices === "function") deviceRestorePromises.push(runWorkerDeviceRestoreWithRetry(addon, context, spec.addonId, nodeId));
529
+ addon.postBrokerStart();
530
+ loadedAddonInstances.set(spec.addonId, addon);
531
+ loaded.push({
532
+ addonId: spec.addonId,
533
+ declaration,
534
+ addon,
535
+ initResult: initResult ?? null
536
+ });
537
+ } catch (err) {
538
+ console.error(`[addon-runner] Init failed for "${spec.addonId}": ${errMsg(err)}`);
539
+ process.exit(1);
540
+ }
541
+ if (deviceRestorePromises.length > 0 && !shuttingDown) await Promise.allSettled(deviceRestorePromises);
542
+ refreshChildCaps("post-init");
543
+ let hubReachableFired = false;
544
+ const fireHubReachableOnce = () => {
545
+ if (hubReachableFired) return;
546
+ hubReachableFired = true;
547
+ for (const entry of loaded) if (typeof entry.addon.onHubReachable === "function") Promise.resolve(entry.addon.onHubReachable()).catch((err) => {
548
+ runnerLog.error(`"${entry.addonId}" onHubReachable() threw`, { meta: { error: errMsg(err) } });
549
+ });
550
+ };
551
+ udsChildClient.onConnected(() => {
552
+ if (hubReachableFired) refreshChildCaps("on reconnect");
553
+ fireHubReachableOnce();
554
+ });
555
+ fireHubReachableOnce();
556
+ runnerLog.info(`"${nodeId}" started — ${loaded.length} addon(s) ready (UDS connected)`);
557
+ process.stdout.write(`READY:${nodeId}\n`);
558
+ const shutdown = async () => {
559
+ shuttingDown = true;
560
+ setWorkerNativeCapsChangeListener(null);
561
+ if (capRefreshTimer !== void 0) {
562
+ clearTimeout(capRefreshTimer);
563
+ capRefreshTimer = void 0;
564
+ }
565
+ runnerLog.info(`"${nodeId}" shutting down ${loaded.length} addon(s)...`);
566
+ for (const entry of loaded) try {
567
+ await entry.addon.shutdown();
568
+ } catch (err) {
569
+ runnerLog.error(`"${entry.addonId}" shutdown threw`, { meta: { error: errMsg(err) } });
570
+ }
571
+ try {
572
+ await udsChildClient.close();
573
+ } catch (err) {
574
+ runnerLog.warn("UDS child transport close threw during shutdown", { meta: { error: errMsg(err) } });
575
+ }
576
+ process.exit(0);
577
+ };
578
+ process.on("SIGTERM", () => {
579
+ shutdown();
580
+ });
581
+ process.on("SIGINT", () => {
582
+ shutdown();
583
+ });
584
+ const initialPpid = process.ppid;
585
+ setInterval(() => {
586
+ if (process.ppid === 1 && initialPpid !== 1) {
587
+ console.error(`[addon-runner] "${nodeId}" parent died (reparented to init) — exiting`);
588
+ shutdown();
589
+ }
590
+ }, 5e3);
591
+ }
592
+ main().catch((err) => {
593
+ console.error(`[addon-runner] Fatal error: ${errMsg(err)}`);
594
+ process.exit(1);
595
+ });
596
+ //#endregion
597
+ export {};
@@ -0,0 +1,26 @@
1
+ import { AuthManager } from './auth-manager.js';
2
+ import { ApiKeyRecord, SettingsStoreClient } from '@camstack/types';
3
+ export type { ApiKeyRecord };
4
+ interface CreateApiKeyInput {
5
+ label: string;
6
+ isAdmin?: boolean;
7
+ allowedProviders?: string[] | '*';
8
+ allowedDevices?: Record<string, string[] | '*'>;
9
+ }
10
+ export interface ApiKeyStorageAccess {
11
+ getStore(): SettingsStoreClient;
12
+ }
13
+ export declare class ApiKeyManager {
14
+ private readonly storageAccess;
15
+ private readonly auth;
16
+ constructor(storageAccess: ApiKeyStorageAccess, auth: AuthManager);
17
+ private get store();
18
+ create(input: CreateApiKeyInput): Promise<{
19
+ record: ApiKeyRecord;
20
+ token: string;
21
+ }>;
22
+ validateToken(token: string): Promise<ApiKeyRecord | null>;
23
+ listAll(): Promise<Omit<ApiKeyRecord, 'tokenHash'>[]>;
24
+ revoke(id: string): Promise<void>;
25
+ findById(id: string): Promise<ApiKeyRecord | null>;
26
+ }