@camstack/server 1.0.0 → 1.0.1

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 (234) hide show
  1. package/{src/agent-status-page.ts → dist/agent-status-page.js} +30 -45
  2. package/dist/api/addon-upload.js +441 -0
  3. package/dist/api/addons-custom.router.js +91 -0
  4. package/dist/api/auth-whoami.js +55 -0
  5. package/dist/api/bridge-addons.router.js +109 -0
  6. package/dist/api/capabilities.router.js +229 -0
  7. package/dist/api/core/addon-settings.router.js +117 -0
  8. package/dist/api/core/agents.router.js +73 -0
  9. package/dist/api/core/auth.router.js +286 -0
  10. package/dist/api/core/bulk-update-coordinator.js +229 -0
  11. package/dist/api/core/cap-providers.js +1124 -0
  12. package/dist/api/core/capabilities.router.js +138 -0
  13. package/dist/api/core/collection-preference.js +17 -0
  14. package/dist/api/core/event-bus-proxy.router.js +45 -0
  15. package/dist/api/core/hwaccel.router.js +91 -0
  16. package/dist/api/core/live-events.router.js +61 -0
  17. package/dist/api/core/logs.router.js +172 -0
  18. package/dist/api/core/notifications.router.js +67 -0
  19. package/dist/api/core/repl.router.js +35 -0
  20. package/dist/api/core/settings-backend.router.js +121 -0
  21. package/dist/api/core/stream-probe.router.js +58 -0
  22. package/dist/api/core/system-events.router.js +100 -0
  23. package/dist/api/health/health.routes.js +68 -0
  24. package/{src/api/oauth2/consent-page.ts → dist/api/oauth2/consent-page.js} +11 -20
  25. package/dist/api/oauth2/oauth2-routes.js +219 -0
  26. package/dist/api/trpc/cap-mount-helpers.js +194 -0
  27. package/dist/api/trpc/cap-route-error-formatter.js +133 -0
  28. package/dist/api/trpc/client-ip.js +147 -0
  29. package/dist/api/trpc/core-cap-bridge.js +115 -0
  30. package/dist/api/trpc/generated-cap-mounts.js +388 -0
  31. package/dist/api/trpc/generated-cap-routers.js +7635 -0
  32. package/dist/api/trpc/scope-access.js +93 -0
  33. package/dist/api/trpc/trpc.context.js +184 -0
  34. package/dist/api/trpc/trpc.middleware.js +139 -0
  35. package/dist/api/trpc/trpc.router.js +188 -0
  36. package/dist/auth/session-cookie.js +47 -0
  37. package/dist/boot/boot-config.js +241 -0
  38. package/dist/boot/integration-id-backfill.js +76 -0
  39. package/dist/boot/post-boot.service.js +85 -0
  40. package/dist/core/addon/addon-call-gateway.js +99 -0
  41. package/dist/core/addon/addon-package.service.js +1560 -0
  42. package/dist/core/addon/addon-registry.service.js +2739 -0
  43. package/{src/core/addon/addon-row-manifest.ts → dist/core/addon/addon-row-manifest.js} +5 -5
  44. package/dist/core/addon/addon-search.service.js +62 -0
  45. package/dist/core/addon/addon-settings-provider.js +102 -0
  46. package/dist/core/addon/addon.tokens.js +5 -0
  47. package/dist/core/addon-bridge/addon-bridge.service.js +145 -0
  48. package/dist/core/addon-pages/addon-pages.service.js +107 -0
  49. package/dist/core/addon-widgets/addon-widgets.service.js +120 -0
  50. package/dist/core/agent/agent-registry.service.js +477 -0
  51. package/dist/core/auth/auth.service.js +10 -0
  52. package/dist/core/capability/capability.service.js +58 -0
  53. package/dist/core/config/config.schema.js +7 -0
  54. package/dist/core/config/config.service.js +10 -0
  55. package/dist/core/events/event-bus.service.js +83 -0
  56. package/dist/core/feature/feature.service.js +10 -0
  57. package/dist/core/lifecycle/lifecycle-state-machine.js +6 -0
  58. package/dist/core/logging/log-ring-buffer.js +6 -0
  59. package/dist/core/logging/logging.service.js +130 -0
  60. package/dist/core/logging/scoped-logger.js +6 -0
  61. package/dist/core/moleculer/cap-call-fn.js +50 -0
  62. package/dist/core/moleculer/cap-route-authority.js +122 -0
  63. package/dist/core/moleculer/moleculer.service.js +898 -0
  64. package/dist/core/network/network-quality.service.js +7 -0
  65. package/dist/core/notification/notification-wrapper.service.js +33 -0
  66. package/dist/core/notification/toast-wrapper.service.js +25 -0
  67. package/dist/core/provider/provider.tokens.js +4 -0
  68. package/dist/core/repl/repl-engine.service.js +140 -0
  69. package/dist/core/storage/fs-storage-backend.js +6 -0
  70. package/dist/core/storage/storage-location-manager.js +6 -0
  71. package/dist/core/storage/storage.service.js +7 -0
  72. package/dist/core/streaming/stream-probe.service.js +209 -0
  73. package/dist/core/topology/topology-emitter.service.js +106 -0
  74. package/dist/launcher.js +325 -0
  75. package/dist/main.js +1098 -0
  76. package/dist/manual-boot.js +227 -0
  77. package/package.json +5 -1
  78. package/src/__tests__/addon-install-e2e.test.ts +0 -74
  79. package/src/__tests__/addon-pages-e2e.test.ts +0 -200
  80. package/src/__tests__/addon-route-session.test.ts +0 -17
  81. package/src/__tests__/addon-settings-router.spec.ts +0 -67
  82. package/src/__tests__/addon-upload.spec.ts +0 -475
  83. package/src/__tests__/agent-registry.spec.ts +0 -179
  84. package/src/__tests__/agent-status-page.spec.ts +0 -82
  85. package/src/__tests__/auth-session-cookie.test.ts +0 -48
  86. package/src/__tests__/bulk-update-coordinator.spec.ts +0 -303
  87. package/src/__tests__/cap-ownership-authority.spec.ts +0 -431
  88. package/src/__tests__/cap-providers/cap-providers-location-import.spec.ts +0 -206
  89. package/src/__tests__/cap-providers/cap-usage-graph.spec.ts +0 -37
  90. package/src/__tests__/cap-providers/compute-topology-categories.spec.ts +0 -110
  91. package/src/__tests__/cap-providers/integrations-delete-cascade.spec.ts +0 -292
  92. package/src/__tests__/cap-providers-bulk-update.spec.ts +0 -408
  93. package/src/__tests__/cap-route-adapter.spec.ts +0 -302
  94. package/src/__tests__/cap-routers/_meta.spec.ts +0 -199
  95. package/src/__tests__/cap-routers/addon-settings.router.spec.ts +0 -115
  96. package/src/__tests__/cap-routers/broker-routing.router.spec.ts +0 -177
  97. package/src/__tests__/cap-routers/cap-route-error-formatter.spec.ts +0 -125
  98. package/src/__tests__/cap-routers/capabilities-node.spec.ts +0 -68
  99. package/src/__tests__/cap-routers/device-link-overlay.spec.ts +0 -137
  100. package/src/__tests__/cap-routers/device-manager-aggregate.router.spec.ts +0 -194
  101. package/src/__tests__/cap-routers/harness.ts +0 -163
  102. package/src/__tests__/cap-routers/metrics-provider.router.spec.ts +0 -133
  103. package/src/__tests__/cap-routers/null-provider-guard.spec.ts +0 -64
  104. package/src/__tests__/cap-routers/pipeline-executor.router.spec.ts +0 -159
  105. package/src/__tests__/cap-routers/settings-store.router.spec.ts +0 -291
  106. package/src/__tests__/capability-e2e.test.ts +0 -384
  107. package/src/__tests__/cli-e2e.test.ts +0 -150
  108. package/src/__tests__/core-cap-bridge.spec.ts +0 -91
  109. package/src/__tests__/dev-bootstrap-shm-ring.spec.ts +0 -40
  110. package/src/__tests__/device-settings-contribution-dispatch.spec.ts +0 -280
  111. package/src/__tests__/embedded-deps-e2e.test.ts +0 -125
  112. package/src/__tests__/event-bus-proxy-router.spec.ts +0 -75
  113. package/src/__tests__/fixtures/mock-analysis-addon-a.ts +0 -37
  114. package/src/__tests__/fixtures/mock-analysis-addon-b.ts +0 -37
  115. package/src/__tests__/fixtures/mock-log-addon.ts +0 -37
  116. package/src/__tests__/fixtures/mock-storage-addon.ts +0 -40
  117. package/src/__tests__/framework-allowlist.spec.ts +0 -96
  118. package/src/__tests__/framework-installer-defer-restart.spec.ts +0 -165
  119. package/src/__tests__/https-e2e.test.ts +0 -124
  120. package/src/__tests__/lifecycle-e2e.test.ts +0 -189
  121. package/src/__tests__/live-events-subscription.spec.ts +0 -149
  122. package/src/__tests__/moleculer/uds-readiness.spec.ts +0 -150
  123. package/src/__tests__/moleculer/uds-topology.spec.ts +0 -418
  124. package/src/__tests__/moleculer/uds-unowned-call.spec.ts +0 -383
  125. package/src/__tests__/moleculer-register-node-idempotency.spec.ts +0 -273
  126. package/src/__tests__/native-cap-route.spec.ts +0 -427
  127. package/src/__tests__/oauth2-account-linking.spec.ts +0 -867
  128. package/src/__tests__/post-boot-restart.spec.ts +0 -161
  129. package/src/__tests__/singleton-contention.test.ts +0 -499
  130. package/src/__tests__/streaming-diagnostic.test.ts +0 -615
  131. package/src/__tests__/streaming-scale.test.ts +0 -314
  132. package/src/__tests__/uds-addon-call-wiring.spec.ts +0 -242
  133. package/src/__tests__/uds-log-ingest.spec.ts +0 -183
  134. package/src/api/__tests__/addons-custom.spec.ts +0 -148
  135. package/src/api/__tests__/capabilities.router.test.ts +0 -56
  136. package/src/api/addon-upload.ts +0 -529
  137. package/src/api/addons-custom.router.ts +0 -101
  138. package/src/api/auth-whoami.ts +0 -101
  139. package/src/api/bridge-addons.router.ts +0 -122
  140. package/src/api/capabilities.router.ts +0 -265
  141. package/src/api/core/__tests__/auth-router-totp.spec.ts +0 -297
  142. package/src/api/core/__tests__/integration-markers.spec.ts +0 -10
  143. package/src/api/core/addon-settings.router.ts +0 -127
  144. package/src/api/core/agents.router.ts +0 -86
  145. package/src/api/core/auth.router.ts +0 -322
  146. package/src/api/core/bulk-update-coordinator.ts +0 -305
  147. package/src/api/core/cap-providers.ts +0 -1339
  148. package/src/api/core/capabilities.router.ts +0 -149
  149. package/src/api/core/collection-preference.ts +0 -40
  150. package/src/api/core/event-bus-proxy.router.ts +0 -45
  151. package/src/api/core/hwaccel.router.ts +0 -108
  152. package/src/api/core/live-events.router.ts +0 -67
  153. package/src/api/core/logs.router.ts +0 -195
  154. package/src/api/core/notifications.router.ts +0 -66
  155. package/src/api/core/repl.router.ts +0 -39
  156. package/src/api/core/settings-backend.router.ts +0 -140
  157. package/src/api/core/stream-probe.router.ts +0 -57
  158. package/src/api/core/system-events.router.ts +0 -125
  159. package/src/api/health/health.routes.ts +0 -117
  160. package/src/api/oauth2/__tests__/oauth2-routes.spec.ts +0 -62
  161. package/src/api/oauth2/oauth2-routes.ts +0 -281
  162. package/src/api/trpc/__tests__/client-ip.spec.ts +0 -146
  163. package/src/api/trpc/__tests__/scope-access-device.spec.ts +0 -268
  164. package/src/api/trpc/__tests__/scope-access.spec.ts +0 -102
  165. package/src/api/trpc/__tests__/webrtc-session-ua-enrich.spec.ts +0 -136
  166. package/src/api/trpc/cap-mount-helpers.ts +0 -245
  167. package/src/api/trpc/cap-route-error-formatter.ts +0 -171
  168. package/src/api/trpc/client-ip.ts +0 -147
  169. package/src/api/trpc/core-cap-bridge.ts +0 -154
  170. package/src/api/trpc/generated-cap-mounts.ts +0 -1240
  171. package/src/api/trpc/generated-cap-routers.ts +0 -11523
  172. package/src/api/trpc/scope-access.ts +0 -110
  173. package/src/api/trpc/trpc.context.ts +0 -258
  174. package/src/api/trpc/trpc.middleware.ts +0 -146
  175. package/src/api/trpc/trpc.router.ts +0 -389
  176. package/src/auth/session-cookie.ts +0 -54
  177. package/src/boot/__tests__/integration-id-backfill.spec.ts +0 -131
  178. package/src/boot/boot-config.ts +0 -259
  179. package/src/boot/integration-id-backfill.ts +0 -109
  180. package/src/boot/post-boot.service.ts +0 -105
  181. package/src/core/addon/__tests__/addon-registry-capability.test.ts +0 -62
  182. package/src/core/addon/__tests__/addon-row-manifest.spec.ts +0 -62
  183. package/src/core/addon/addon-call-gateway.ts +0 -171
  184. package/src/core/addon/addon-package.service.ts +0 -1787
  185. package/src/core/addon/addon-registry.service.ts +0 -3130
  186. package/src/core/addon/addon-search.service.ts +0 -91
  187. package/src/core/addon/addon-settings-provider.ts +0 -220
  188. package/src/core/addon/addon.tokens.ts +0 -2
  189. package/src/core/addon-bridge/addon-bridge.service.ts +0 -130
  190. package/src/core/addon-pages/addon-pages.service.spec.ts +0 -117
  191. package/src/core/addon-pages/addon-pages.service.ts +0 -82
  192. package/src/core/addon-widgets/addon-widgets.service.ts +0 -95
  193. package/src/core/agent/agent-registry.service.ts +0 -529
  194. package/src/core/auth/auth.service.spec.ts +0 -86
  195. package/src/core/auth/auth.service.ts +0 -8
  196. package/src/core/capability/capability.service.ts +0 -66
  197. package/src/core/config/config.schema.ts +0 -3
  198. package/src/core/config/config.service.spec.ts +0 -175
  199. package/src/core/config/config.service.ts +0 -7
  200. package/src/core/events/event-bus.service.spec.ts +0 -235
  201. package/src/core/events/event-bus.service.ts +0 -89
  202. package/src/core/feature/feature.service.spec.ts +0 -99
  203. package/src/core/feature/feature.service.ts +0 -8
  204. package/src/core/lifecycle/lifecycle-state-machine.spec.ts +0 -166
  205. package/src/core/lifecycle/lifecycle-state-machine.ts +0 -3
  206. package/src/core/logging/log-ring-buffer.ts +0 -3
  207. package/src/core/logging/logging.service.spec.ts +0 -287
  208. package/src/core/logging/logging.service.ts +0 -143
  209. package/src/core/logging/scoped-logger.ts +0 -3
  210. package/src/core/moleculer/cap-call-fn.spec.ts +0 -173
  211. package/src/core/moleculer/cap-call-fn.ts +0 -107
  212. package/src/core/moleculer/cap-route-authority.ts +0 -194
  213. package/src/core/moleculer/moleculer.service.ts +0 -1072
  214. package/src/core/network/network-quality.service.spec.ts +0 -53
  215. package/src/core/network/network-quality.service.ts +0 -5
  216. package/src/core/notification/notification-wrapper.service.ts +0 -34
  217. package/src/core/notification/toast-wrapper.service.ts +0 -27
  218. package/src/core/provider/provider.tokens.ts +0 -1
  219. package/src/core/repl/repl-engine.service.spec.ts +0 -444
  220. package/src/core/repl/repl-engine.service.ts +0 -155
  221. package/src/core/storage/fs-storage-backend.spec.ts +0 -70
  222. package/src/core/storage/fs-storage-backend.ts +0 -3
  223. package/src/core/storage/storage-location-manager.spec.ts +0 -130
  224. package/src/core/storage/storage-location-manager.ts +0 -3
  225. package/src/core/storage/storage.service.spec.ts +0 -73
  226. package/src/core/storage/storage.service.ts +0 -3
  227. package/src/core/streaming/stream-probe.service.ts +0 -221
  228. package/src/core/topology/topology-emitter.service.ts +0 -105
  229. package/src/launcher.ts +0 -314
  230. package/src/main.ts +0 -1245
  231. package/src/manual-boot.ts +0 -301
  232. package/tsconfig.build.json +0 -8
  233. package/tsconfig.json +0 -33
  234. package/vitest.config.ts +0 -26
@@ -1,3 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.overlayDeclaration = overlayDeclaration;
1
4
  /**
2
5
  * Overlay the fresh on-disk `declaration` onto a live addon instance's
3
6
  * `manifest` when building the rows returned by `listAddons()`.
@@ -21,9 +24,6 @@
21
24
  * values), so the spread overlays only the keys the declaration actually
22
25
  * defines; the instance manifest fills any gaps.
23
26
  */
24
- export function overlayDeclaration<M extends object>(
25
- instanceManifest: M,
26
- declaration: Partial<M> | undefined,
27
- ): M {
28
- return { ...instanceManifest, ...declaration }
27
+ function overlayDeclaration(instanceManifest, declaration) {
28
+ return { ...instanceManifest, ...declaration };
29
29
  }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AddonSearchService = void 0;
4
+ const types_1 = require("@camstack/types");
5
+ class AddonSearchService {
6
+ loggingService;
7
+ cache = null;
8
+ CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
9
+ constructor(loggingService) {
10
+ this.loggingService = loggingService;
11
+ }
12
+ async searchAddons(query) {
13
+ const npmResults = await this.fetchFromNpm();
14
+ // Filter by additional query if provided
15
+ let filtered = npmResults;
16
+ if (query) {
17
+ const q = query.toLowerCase();
18
+ filtered = npmResults.filter((r) => r.name.toLowerCase().includes(q) ||
19
+ r.description?.toLowerCase().includes(q) ||
20
+ r.keywords?.some((k) => k.toLowerCase().includes(q)));
21
+ }
22
+ // Return without install status — the caller can merge with installed list
23
+ return filtered.map((r) => ({
24
+ name: r.name,
25
+ version: r.version,
26
+ description: r.description ?? '',
27
+ keywords: r.keywords ?? [],
28
+ publishedAt: r.date ?? '',
29
+ author: r.publisher?.username ?? '',
30
+ installed: false, // caller will enrich this
31
+ installedVersion: undefined,
32
+ }));
33
+ }
34
+ async fetchFromNpm() {
35
+ // Return from cache if still fresh
36
+ if (this.cache && Date.now() - this.cache.timestamp < this.CACHE_TTL_MS) {
37
+ return this.cache.results;
38
+ }
39
+ // npm registry v1 search: packages with BOTH "camstack" and "addon" keywords
40
+ const url = 'https://registry.npmjs.org/-/v1/search?text=keywords:camstack+keywords:addon&size=250';
41
+ try {
42
+ const response = await fetch(url, {
43
+ headers: { Accept: 'application/json' },
44
+ signal: AbortSignal.timeout(10000),
45
+ });
46
+ if (!response.ok) {
47
+ throw new Error(`npm search failed: ${response.status}`);
48
+ }
49
+ const data = (await response.json());
50
+ const results = data.objects.map((o) => o.package);
51
+ this.cache = { results, timestamp: Date.now() };
52
+ return results;
53
+ }
54
+ catch (err) {
55
+ const logger = this.loggingService.createLogger('addon-search');
56
+ logger.warn('npm search failed', { meta: { error: (0, types_1.errMsg)(err) } });
57
+ // Stale cache is better than nothing on transient network failure
58
+ return this.cache?.results ?? [];
59
+ }
60
+ }
61
+ }
62
+ exports.AddonSearchService = AddonSearchService;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAddonSettingsProvider = createAddonSettingsProvider;
4
+ function reshapeForOutput(schema) {
5
+ return {
6
+ tabs: schema.tabs
7
+ ? schema.tabs.map((t) => ({ id: t.id, label: t.label, icon: t.icon, order: t.order }))
8
+ : undefined,
9
+ sections: schema.sections.map((s) => ({
10
+ id: s.id,
11
+ title: s.title,
12
+ description: s.description,
13
+ style: s.style,
14
+ defaultCollapsed: s.defaultCollapsed,
15
+ columns: s.columns,
16
+ tab: s.tab,
17
+ order: s.order,
18
+ fields: [...s.fields],
19
+ })),
20
+ };
21
+ }
22
+ /**
23
+ * True for Moleculer's `ServiceNotFoundError` — raised when a
24
+ * `<addonId>.settings.*` action is called before the owning forked
25
+ * addon has registered its service (still booting, or wedged in
26
+ * `onInitialize`). Matched by `name` so we don't depend on Moleculer's
27
+ * error class being importable here.
28
+ */
29
+ function isServiceNotFoundError(err) {
30
+ return err instanceof Error && err.name === 'ServiceNotFoundError';
31
+ }
32
+ function createAddonSettingsProvider(deps) {
33
+ const { getAddon, gateway } = deps;
34
+ // Forked READ — route through the shared gateway (UDS hub-local-child OR
35
+ // Moleculer remote agent). Degrades to null when the forked addon can't
36
+ // answer yet (mid-boot / missing service) so the panel shows its empty state
37
+ // instead of bubbling a 500.
38
+ async function forkedGet(addonId, method, args, nodeId) {
39
+ try {
40
+ const result = await gateway.callForked(addonId, { target: 'settings', method, args }, nodeId);
41
+ return result ? reshapeForOutput(result) : null;
42
+ }
43
+ catch (err) {
44
+ if (isServiceNotFoundError(err))
45
+ return null;
46
+ throw err;
47
+ }
48
+ }
49
+ // Forked UPDATE — route through the shared gateway; returns the ack.
50
+ async function forkedUpdate(addonId, method, args, nodeId) {
51
+ await gateway.callForked(addonId, { target: 'settings', method, args }, nodeId);
52
+ return { success: true };
53
+ }
54
+ // ── Provider implementation ──────────────────────────────────────
55
+ return {
56
+ async getGlobalSettings(input) {
57
+ if (gateway.isInProcess(input.addonId, input.nodeId)) {
58
+ const addon = getAddon(input.addonId);
59
+ if (!addon || typeof addon.getGlobalSettings !== 'function')
60
+ return null;
61
+ const result = await addon.getGlobalSettings(input.overlay, input.cap);
62
+ return result ? reshapeForOutput(result) : null;
63
+ }
64
+ return forkedGet(input.addonId, 'getGlobalSettings', {
65
+ ...(input.overlay ? { overlay: input.overlay } : {}),
66
+ ...(input.cap ? { cap: input.cap } : {}),
67
+ }, input.nodeId);
68
+ },
69
+ async updateGlobalSettings(input) {
70
+ if (gateway.isInProcess(input.addonId, input.nodeId)) {
71
+ const addon = getAddon(input.addonId);
72
+ if (!addon || typeof addon.updateGlobalSettings !== 'function') {
73
+ throw new Error(`Addon "${input.addonId}" does not implement updateGlobalSettings`);
74
+ }
75
+ await addon.updateGlobalSettings(input.patch);
76
+ return { success: true };
77
+ }
78
+ return forkedUpdate(input.addonId, 'updateGlobalSettings', { patch: input.patch }, input.nodeId);
79
+ },
80
+ async getDeviceSettings(input) {
81
+ if (gateway.isInProcess(input.addonId, input.nodeId)) {
82
+ const addon = getAddon(input.addonId);
83
+ if (!addon || typeof addon.getDeviceSettings !== 'function')
84
+ return null;
85
+ const result = await addon.getDeviceSettings(input.deviceId);
86
+ return result ? reshapeForOutput(result) : null;
87
+ }
88
+ return forkedGet(input.addonId, 'getDeviceSettings', { deviceId: input.deviceId }, input.nodeId);
89
+ },
90
+ async updateDeviceSettings(input) {
91
+ if (gateway.isInProcess(input.addonId, input.nodeId)) {
92
+ const addon = getAddon(input.addonId);
93
+ if (!addon || typeof addon.updateDeviceSettings !== 'function') {
94
+ throw new Error(`Addon "${input.addonId}" does not implement updateDeviceSettings`);
95
+ }
96
+ await addon.updateDeviceSettings(input.deviceId, input.patch);
97
+ return { success: true };
98
+ }
99
+ return forkedUpdate(input.addonId, 'updateDeviceSettings', { deviceId: input.deviceId, patch: input.patch }, input.nodeId);
100
+ },
101
+ };
102
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BUILTIN_ADDONS = exports.ADDON_REGISTRY = void 0;
4
+ exports.ADDON_REGISTRY = Symbol('ADDON_REGISTRY');
5
+ exports.BUILTIN_ADDONS = Symbol('BUILTIN_ADDONS');
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AddonBridgeService = void 0;
37
+ const path = __importStar(require("node:path"));
38
+ const types_1 = require("@camstack/types");
39
+ /**
40
+ * AddonBridgeService — slim package-management surface.
41
+ *
42
+ * Historically this service also cached per-camera pipeline configs + per-
43
+ * addon config records + hosted an in-process `PipelineRunner` that consumers
44
+ * called via `processFrame` / `processMotionFrame`. Every piece of that
45
+ * plumbing was dead post commit 2c (the runtime scheduler moved to
46
+ * `addon-pipeline-runner`) and the pipeline page redesign finishes the job
47
+ * by deleting the cache, the AddonEngineManager wiring, the bridge-pipeline
48
+ * tRPC router, and the PipelineWiringModule that bolted the legacy addon
49
+ * resolver onto this service.
50
+ *
51
+ * What's left is ONLY the addon package management surface: the installer
52
+ * + the loader + `reloadPackages` + `listAvailableAddons`. These are still
53
+ * used by:
54
+ * - `server/backend/src/api/addon-upload.ts` (multipart upload → install
55
+ * → loader reload)
56
+ * - `server/backend/src/api/bridge-addons.router.ts` (install/uninstall
57
+ * package lifecycle, addon list via the loader)
58
+ */
59
+ class AddonBridgeService {
60
+ loggingService;
61
+ logger;
62
+ loader;
63
+ installer = null;
64
+ /** Whether the bridge initialised successfully */
65
+ available = false;
66
+ constructor(loggingService) {
67
+ this.loggingService = loggingService;
68
+ this.logger = this.loggingService.createLogger('AddonBridge');
69
+ // Initialize installer eagerly in constructor (no async needed).
70
+ // This ensures install/uninstall works even before onModuleInit completes.
71
+ try {
72
+ const kernel = require('@camstack/kernel');
73
+ const dataDir = process.env.CAMSTACK_DATA ?? 'camstack-data';
74
+ const addonsDir = path.resolve(dataDir, 'addons');
75
+ const workspacePackagesDir = kernel.detectWorkspacePackagesDir(__dirname);
76
+ this.installer = new kernel.AddonInstaller({
77
+ addonsDir,
78
+ workspacePackagesDir: workspacePackagesDir ?? undefined,
79
+ });
80
+ kernel.ensureDir(addonsDir);
81
+ }
82
+ catch (error) {
83
+ const msg = (0, types_1.errMsg)(error);
84
+ this.logger.warn('Installer init failed', { meta: { error: msg } });
85
+ }
86
+ }
87
+ async onModuleInit() {
88
+ this.logger.info('Initializing addon bridge (package management only)...');
89
+ try {
90
+ const kernel = await Promise.resolve().then(() => __importStar(require('@camstack/kernel')));
91
+ this.loader = new kernel.AddonLoader(this.logger.child('AddonLoader'));
92
+ const dataDir = process.env.CAMSTACK_DATA ?? 'camstack-data';
93
+ const addonsDir = path.resolve(dataDir, 'addons');
94
+ await this.loader.loadFromDirectory(addonsDir);
95
+ this.available = true;
96
+ this.logger.info('Addon bridge initialized', {
97
+ meta: { count: this.loader.listAddons().length },
98
+ });
99
+ }
100
+ catch (error) {
101
+ const msg = (0, types_1.errMsg)(error);
102
+ this.logger.warn('Addon bridge loader failed — install/uninstall still available', {
103
+ meta: { error: msg },
104
+ });
105
+ }
106
+ }
107
+ async onModuleDestroy() {
108
+ this.logger.info('Addon bridge shut down');
109
+ }
110
+ // ---------------------------------------------------------------------------
111
+ // Public API
112
+ // ---------------------------------------------------------------------------
113
+ /** Whether the bridge is ready for use */
114
+ isAvailable() {
115
+ return this.available;
116
+ }
117
+ /** Return the underlying AddonLoader (for querying / introspection) */
118
+ getLoader() {
119
+ return this.loader;
120
+ }
121
+ /** List IDs of all successfully loaded addons */
122
+ listAvailableAddons() {
123
+ if (!this.available)
124
+ return [];
125
+ return this.loader.listAddons().map((a) => a.declaration.id);
126
+ }
127
+ /** Return the AddonInstaller instance (may be null if bridge failed to init) */
128
+ getInstaller() {
129
+ return this.installer;
130
+ }
131
+ /** Re-discover addons from the addons directory (call after install/uninstall) */
132
+ async reloadPackages() {
133
+ if (!this.available) {
134
+ this.logger.warn('reloadPackages called but bridge is unavailable — skipping');
135
+ return;
136
+ }
137
+ const kernel = await Promise.resolve().then(() => __importStar(require('@camstack/kernel')));
138
+ this.loader = new kernel.AddonLoader(this.logger.child('AddonLoader'));
139
+ const dataDir = process.env.CAMSTACK_DATA ?? 'camstack-data';
140
+ const addonsDir = path.resolve(dataDir, 'addons');
141
+ await this.loader.loadFromDirectory(addonsDir);
142
+ this.logger.info('Reloaded', { meta: { count: this.loader.listAddons().length } });
143
+ }
144
+ }
145
+ exports.AddonBridgeService = AddonBridgeService;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AddonPagesService = void 0;
37
+ const path = __importStar(require("node:path"));
38
+ const fs = __importStar(require("node:fs"));
39
+ /**
40
+ * AddonPagesService — server-side helper that backs the static file
41
+ * route `/api/addon-pages/:addonId/*` (registered in `main.ts`).
42
+ *
43
+ * The public listing surface (`addonPages.listPages` on the AppRouter)
44
+ * has been split out of this class — it now lives in the
45
+ * `addon-pages-aggregator` builtin (`@camstack/core/builtins/...`)
46
+ * which walks every `addon-pages-source` collection provider and
47
+ * stamps versioned `bundleUrl`s. The split lets both ends flow through
48
+ * codegen instead of relying on a hand-written wrapper.
49
+ *
50
+ * What stays here: filesystem path resolution + traversal protection
51
+ * for the static file route. Both rely on `CapabilityService` to
52
+ * enumerate registered page providers (so unknown / unregistered
53
+ * addons can't be probed via path traversal tricks) and on
54
+ * `ConfigService` to locate the addons directory.
55
+ */
56
+ class AddonPagesService {
57
+ loggingService;
58
+ configService;
59
+ caps;
60
+ logger;
61
+ constructor(loggingService, configService, caps) {
62
+ this.loggingService = loggingService;
63
+ this.configService = configService;
64
+ this.caps = caps;
65
+ this.logger = this.loggingService.createLogger('AddonPagesService');
66
+ }
67
+ /**
68
+ * Resolve the filesystem path to an addon's page bundle file.
69
+ * Returns null if the addon is not registered, the file doesn't exist,
70
+ * or the path would escape the addon directory (path traversal protection).
71
+ */
72
+ resolveBundle(addonId, filePath) {
73
+ // Check if the addon is a registered page provider via the
74
+ // collection cap. `addon-pages-source` is the new home for the raw
75
+ // per-provider declarations (the public `addon-pages` cap is the
76
+ // aggregated singleton — different shape, different surface).
77
+ const providers = this.caps.getCollection('addon-pages-source');
78
+ const isRegistered = providers.some((p) => p.id === addonId);
79
+ if (!isRegistered) {
80
+ this.logger.warn('Bundle resolve failed: addon not registered as page provider', {
81
+ tags: { addonId },
82
+ });
83
+ return null;
84
+ }
85
+ const addonsDir = this.resolveAddonsDir();
86
+ // Addon packages are at addons/@camstack/addon-<id>/dist/
87
+ const addonDistPath = path.join(addonsDir, '@camstack', `addon-${addonId}`, 'dist');
88
+ const resolvedBase = path.resolve(addonDistPath);
89
+ const resolvedFile = path.resolve(addonDistPath, filePath);
90
+ // Path traversal protection
91
+ if (!resolvedFile.startsWith(resolvedBase + path.sep) && resolvedFile !== resolvedBase) {
92
+ this.logger.warn('Path traversal denied for addon', { tags: { addonId }, meta: { filePath } });
93
+ return null;
94
+ }
95
+ if (!fs.existsSync(resolvedFile)) {
96
+ this.logger.debug('Bundle file not found', { meta: { resolvedFile } });
97
+ return null;
98
+ }
99
+ return resolvedFile;
100
+ }
101
+ /** Resolve the addons directory from config */
102
+ resolveAddonsDir() {
103
+ const dataPath = this.configService.get('server.dataPath') ?? 'camstack-data';
104
+ return path.resolve(dataPath, 'addons');
105
+ }
106
+ }
107
+ exports.AddonPagesService = AddonPagesService;
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AddonWidgetsService = void 0;
37
+ const path = __importStar(require("node:path"));
38
+ const fs = __importStar(require("node:fs"));
39
+ /**
40
+ * Strip the `@<nodeId>` suffix the CapabilityBridge appends to
41
+ * collection-provider registry keys for cross-node addons. The widget
42
+ * static-file route always receives the bare manifest id.
43
+ */
44
+ function stripNodeSuffix(registryKey) {
45
+ const at = registryKey.indexOf('@');
46
+ return at === -1 ? registryKey : registryKey.slice(0, at);
47
+ }
48
+ /**
49
+ * AddonWidgetsService — server-side helper that backs the static file
50
+ * route `/api/addon-widgets/:addonId/*` (registered in `main.ts`).
51
+ *
52
+ * Mirrors `AddonPagesService` exactly. The public listing surface
53
+ * (`addonWidgets.listWidgets` on the AppRouter) lives in the
54
+ * `addon-widgets-aggregator` builtin which walks every
55
+ * `addon-widgets-source` collection provider and stamps versioned
56
+ * `bundleUrl`s. Both ends flow through codegen.
57
+ *
58
+ * What stays here: filesystem path resolution + traversal protection
59
+ * for the static file route. The registered-provider check uses
60
+ * `CapabilityService` (so unknown / unregistered addons can't be
61
+ * probed via path traversal tricks); the on-disk path comes from
62
+ * `AddonRegistryService.getAddonInstallPath()` so bundled addons
63
+ * (where one npm package ships multiple addon entries under
64
+ * `dist/<entryId>/`) resolve to the right sub-folder instead of the
65
+ * pre-bundle `addons/@camstack/addon-<id>/dist/` layout.
66
+ */
67
+ class AddonWidgetsService {
68
+ loggingService;
69
+ caps;
70
+ registry;
71
+ logger;
72
+ constructor(loggingService, caps, registry) {
73
+ this.loggingService = loggingService;
74
+ this.caps = caps;
75
+ this.registry = registry;
76
+ this.logger = this.loggingService.createLogger('AddonWidgetsService');
77
+ }
78
+ /**
79
+ * Resolve the filesystem path to an addon's widget bundle file.
80
+ * Returns null if the addon is not registered, the file doesn't exist,
81
+ * or the path would escape the addon directory (path traversal protection).
82
+ */
83
+ resolveBundle(addonId, filePath) {
84
+ // The collection cap doesn't carry an `id` on its provider, so we
85
+ // attribute via `getCollectionEntries` (returns `[addonId, provider]`).
86
+ //
87
+ // For cross-node addons the CapabilityBridge registers the provider
88
+ // under a node-qualified key `<addonId>@<nodeId>` (see
89
+ // `moleculer.service.ts`). The bundle is hub-resident and keyed by
90
+ // the bare manifest id, so the route always carries the bare
91
+ // `addonId` — match it against each registry key with the
92
+ // `@<nodeId>` suffix stripped.
93
+ const entries = this.caps.getCollectionEntries('addon-widgets-source');
94
+ const isRegistered = entries.some(([id]) => stripNodeSuffix(id) === addonId);
95
+ if (!isRegistered) {
96
+ this.logger.warn('Bundle resolve failed: addon not registered as widget provider', {
97
+ tags: { addonId },
98
+ });
99
+ return null;
100
+ }
101
+ const installPath = this.registry.getAddonInstallPath(addonId);
102
+ if (!installPath) {
103
+ this.logger.warn('Bundle resolve failed: addon install path not found', { tags: { addonId } });
104
+ return null;
105
+ }
106
+ const resolvedBase = path.resolve(installPath.distDir);
107
+ const resolvedFile = path.resolve(installPath.distDir, filePath);
108
+ // Path traversal protection
109
+ if (!resolvedFile.startsWith(resolvedBase + path.sep) && resolvedFile !== resolvedBase) {
110
+ this.logger.warn('Path traversal denied for addon', { tags: { addonId }, meta: { filePath } });
111
+ return null;
112
+ }
113
+ if (!fs.existsSync(resolvedFile)) {
114
+ this.logger.debug('Bundle file not found', { meta: { resolvedFile } });
115
+ return null;
116
+ }
117
+ return resolvedFile;
118
+ }
119
+ }
120
+ exports.AddonWidgetsService = AddonWidgetsService;