@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,149 +0,0 @@
1
- /**
2
- * Capability admin router — fixed core API (not a capability).
3
- *
4
- * Lets admins inspect the capability registry and set system/device
5
- * overrides. Wraps `CapabilityRegistry` directly (kernel primitive).
6
- */
7
- import { z } from 'zod'
8
- import type { CapabilityRegistry } from '@camstack/kernel'
9
- import type { ConfigService } from '../../core/config/config.service.js'
10
- import { trpcRouter, adminProcedure } from '../trpc/trpc.middleware.js'
11
-
12
- export function createCapabilitiesRouter(
13
- registry: CapabilityRegistry | null,
14
- config: ConfigService,
15
- ) {
16
- return trpcRouter({
17
- listCapabilities: adminProcedure
18
- .input(z.void())
19
- .query(() => registry?.listCapabilities() ?? []),
20
-
21
- getCapability: adminProcedure.input(z.object({ name: z.string() })).query(({ input }) => {
22
- if (!registry) return null
23
- return (
24
- registry.listCapabilities().find((c: { name: string }) => c.name === input.name) ?? null
25
- )
26
- }),
27
-
28
- setActiveSingleton: adminProcedure
29
- .input(
30
- z.object({
31
- capability: z.string(),
32
- addonId: z.string(),
33
- nodeId: z.string().optional(),
34
- }),
35
- )
36
- .output(z.void())
37
- .mutation(async ({ input }) => {
38
- if (!registry) throw new Error('Capability registry unavailable')
39
- await registry.setActiveSingleton(input.capability, input.addonId, input.nodeId)
40
- if (input.nodeId !== undefined) {
41
- // Per-node override: persist under the node-qualified key so the boot
42
- // restorer (`setNodeConfigReader`) replays it on restart.
43
- config.set(
44
- `capabilities.singletonNode.${input.capability}.${input.nodeId}`,
45
- input.addonId,
46
- )
47
- } else {
48
- // Cluster-global default: persist to the SAME key the boot restorer reads
49
- // (addon-registry `setConfigReader` → `capabilities.singleton.<cap>`).
50
- config.set(`capabilities.singleton.${input.capability}`, input.addonId)
51
- }
52
- }),
53
-
54
- clearSingletonNodeOverride: adminProcedure
55
- .input(z.object({ capability: z.string(), nodeId: z.string() }))
56
- .output(z.void())
57
- .mutation(({ input }) => {
58
- if (!registry) throw new Error('Capability registry unavailable')
59
- registry.clearSingletonNodeOverride(input.capability, input.nodeId)
60
- // Persist the cleared state as null so the boot restorer doesn't re-apply
61
- // a stale override on restart.
62
- config.set(`capabilities.singletonNode.${input.capability}.${input.nodeId}`, null)
63
- }),
64
-
65
- /**
66
- * Set the enabled set of providers for a collection capability in
67
- * one shot. Providers already registered on the capability that are
68
- * NOT in `enabledAddonIds` get disabled; the ones in the list are
69
- * re-enabled. Other collections are untouched. Intended as the
70
- * save endpoint for a bulk multi-select UI.
71
- */
72
- setCollectionEnabledProviders: adminProcedure
73
- .input(
74
- z.object({
75
- capability: z.string(),
76
- enabledAddonIds: z.array(z.string()),
77
- }),
78
- )
79
- .output(z.void())
80
- .mutation(({ input }) => {
81
- if (!registry) throw new Error('Capability registry unavailable')
82
- const caps = registry.listCapabilities()
83
- const entry = caps.find((c) => c.name === input.capability)
84
- if (!entry) throw new Error(`Capability "${input.capability}" not declared`)
85
- if (entry.mode !== 'collection') {
86
- throw new Error(`Capability "${input.capability}" is not a collection`)
87
- }
88
- const wanted = new Set(input.enabledAddonIds)
89
- for (const providerId of entry.providers) {
90
- if (wanted.has(providerId)) {
91
- registry.enableCollectionProvider(input.capability, providerId)
92
- } else {
93
- registry.disableCollectionProvider(input.capability, providerId)
94
- }
95
- }
96
- }),
97
-
98
- setDeviceCapability: adminProcedure
99
- .input(z.object({ deviceId: z.string(), capability: z.string(), addonId: z.string() }))
100
- .output(z.void())
101
- .mutation(({ input }) => {
102
- if (!registry) throw new Error('Capability registry unavailable')
103
- registry.setDeviceOverride(input.deviceId, input.capability, input.addonId)
104
- }),
105
-
106
- clearDeviceCapability: adminProcedure
107
- .input(z.object({ deviceId: z.string(), capability: z.string() }))
108
- .output(z.void())
109
- .mutation(({ input }) => {
110
- if (!registry) throw new Error('Capability registry unavailable')
111
- registry.clearDeviceOverride(input.deviceId, input.capability)
112
- }),
113
-
114
- getDeviceCapabilities: adminProcedure
115
- .input(z.object({ deviceId: z.string() }))
116
- .output(z.record(z.string(), z.string()))
117
- .query(({ input }) => {
118
- if (!registry) return {}
119
- const overrides = registry.getDeviceOverrides(input.deviceId)
120
- const result: Record<string, string> = {}
121
- for (const [cap, addonId] of overrides) {
122
- result[cap] = addonId
123
- }
124
- return result
125
- }),
126
-
127
- setDeviceCollectionFilter: adminProcedure
128
- .input(
129
- z.object({
130
- deviceId: z.string(),
131
- capability: z.string(),
132
- addonIds: z.array(z.string()),
133
- }),
134
- )
135
- .output(z.void())
136
- .mutation(({ input }) => {
137
- if (!registry) throw new Error('Capability registry unavailable')
138
- registry.setDeviceCollectionFilter(input.deviceId, input.capability, input.addonIds)
139
- }),
140
-
141
- clearDeviceCollectionFilter: adminProcedure
142
- .input(z.object({ deviceId: z.string(), capability: z.string() }))
143
- .output(z.void())
144
- .mutation(({ input }) => {
145
- if (!registry) throw new Error('Capability registry unavailable')
146
- registry.clearDeviceCollectionFilter(input.deviceId, input.capability)
147
- }),
148
- })
149
- }
@@ -1,40 +0,0 @@
1
- /**
2
- * Single canonical writer for the collection-capability enable/disable
3
- * preference.
4
- *
5
- * A collection capability can have individual providers disabled by an
6
- * operator. The disabled-set must survive a hub reboot — otherwise a
7
- * provider an operator deliberately disabled (TURN relay, an auth or
8
- * export backend, …) silently re-enables on the next restart.
9
- *
10
- * The persistence key/format is `capabilities.collection.<capName>` holding
11
- * `JSON.stringify({ disabled: string[] })`. This module is the ONE place
12
- * that writes that key so the format never drifts between the two callers
13
- * (`capabilities` core router and the `addons` cap's
14
- * `setCapabilityProviderEnabled`). The kernel `CapabilityRegistry` reads
15
- * it back at boot through the `CollectionConfigReader` hook wired in
16
- * `addon-registry.service.ts`.
17
- */
18
- import type { ConfigService } from '../../core/config/config.service'
19
-
20
- /** ConfigService key holding the persisted disabled-set for a collection cap. */
21
- export function collectionPreferenceKey(capability: string): string {
22
- return `capabilities.collection.${capability}`
23
- }
24
-
25
- /**
26
- * Persist the disabled-addonId set for a collection capability. Pass the
27
- * authoritative set straight from `registry.listCapabilities()` after the
28
- * enable/disable mutation so the stored value always mirrors the live
29
- * registry state.
30
- */
31
- export function persistCollectionDisabled(
32
- configService: ConfigService,
33
- capability: string,
34
- disabled: readonly string[],
35
- ): void {
36
- configService.set(
37
- collectionPreferenceKey(capability),
38
- JSON.stringify({ disabled: [...disabled] }),
39
- )
40
- }
@@ -1,45 +0,0 @@
1
- /**
2
- * EventBus proxy router — allows forked workers to emit events to the
3
- * hub's EventBus via tRPC.
4
- *
5
- * Workers call `trpc.eventBusProxy.emit.mutate(event)` from their
6
- * `context.eventBus.emit()` implementation. The hub-side router
7
- * deserializes the event and emits it on the real EventBus.
8
- *
9
- * Subscribe/getRecent are routed through the existing `live.onEvent`
10
- * subscription and `events` query routers — no duplication needed here.
11
- *
12
- * Introduced in session 7 (EventBus wiring for forked addons).
13
- */
14
- import { z } from 'zod'
15
- import type { IEventBus } from '@camstack/types'
16
- import { trpcRouter, protectedProcedure } from '../trpc/trpc.middleware.js'
17
-
18
- const SystemEventInputSchema = z.object({
19
- id: z.string(),
20
- timestamp: z.string(), // ISO 8601 — converted to Date on emit
21
- source: z.object({
22
- type: z.string(),
23
- id: z.string(),
24
- }),
25
- category: z.string(),
26
- data: z.record(z.string(), z.unknown()),
27
- })
28
-
29
- export function createEventBusProxyRouter(eventBus: IEventBus) {
30
- return trpcRouter({
31
- emit: protectedProcedure
32
- .input(SystemEventInputSchema)
33
- .output(z.object({ ok: z.literal(true) }))
34
- .mutation(({ input }) => {
35
- eventBus.emit({
36
- id: input.id,
37
- timestamp: new Date(input.timestamp),
38
- source: { type: input.source.type, id: input.source.id },
39
- category: input.category,
40
- data: input.data,
41
- })
42
- return { ok: true as const }
43
- }),
44
- })
45
- }
@@ -1,108 +0,0 @@
1
- /**
2
- * Hwaccel router — fixed core API (not a capability).
3
- *
4
- * Thin wrapper around the per-node `$hwaccel.resolve` Moleculer
5
- * service. The hub-local instance (same process) serves the default
6
- * `resolve` call; the per-node `resolveForNode` action targets any
7
- * node in the cluster — used by the admin UI pipeline / NodeDetail
8
- * pages to show the hwaccel backends available on each agent.
9
- *
10
- * Cross-node behaviour: `resolveForNode({ nodeId })` forwards via
11
- * `broker.call('$hwaccel.resolve', params, { nodeID })`. Every node
12
- * (hub + forked worker + remote agent) registers `$hwaccel` at
13
- * bootstrap, so any reachable nodeId works.
14
- */
15
- import { z } from 'zod'
16
- import type { ServiceBroker } from 'moleculer'
17
- import type { HwAccelResolution } from '@camstack/types'
18
- import { trpcRouter, adminProcedure } from '../trpc/trpc.middleware.js'
19
-
20
- const HwAccelBackendSchema = z.enum([
21
- 'videotoolbox',
22
- 'cuda',
23
- 'nvdec',
24
- 'vaapi',
25
- 'qsv',
26
- 'd3d11va',
27
- 'dxva2',
28
- 'amf',
29
- 'vdpau',
30
- 'drm',
31
- ])
32
-
33
- const HwAccelPreferSchema = z
34
- .union([HwAccelBackendSchema, z.literal('none')])
35
- .nullable()
36
- .optional()
37
-
38
- const HwAccelResolutionSchema = z.object({
39
- preferred: z.array(HwAccelBackendSchema).readonly(),
40
- rationale: z.string(),
41
- })
42
-
43
- export function createHwAccelRouter(broker: ServiceBroker | null) {
44
- return trpcRouter({
45
- /** Probe the current hub process. */
46
- resolve: adminProcedure
47
- .input(z.object({ prefer: HwAccelPreferSchema }).optional())
48
- .output(HwAccelResolutionSchema)
49
- .query(async ({ input }) => {
50
- if (!broker) throw new Error('Moleculer broker not available')
51
- const params = { prefer: input?.prefer ?? null }
52
- return broker.call('$hwaccel.resolve', params) as Promise<HwAccelResolution>
53
- }),
54
-
55
- /** Probe a specific node in the cluster — used by per-agent UI cards. */
56
- resolveForNode: adminProcedure
57
- .input(z.object({ nodeId: z.string(), prefer: HwAccelPreferSchema }))
58
- .output(HwAccelResolutionSchema)
59
- .query(async ({ input }) => {
60
- if (!broker) throw new Error('Moleculer broker not available')
61
- const params = { prefer: input.prefer ?? null }
62
- return broker.call('$hwaccel.resolve', params, {
63
- nodeID: input.nodeId,
64
- }) as Promise<HwAccelResolution>
65
- }),
66
-
67
- /** List every node currently reachable and the hwaccel each resolves to. */
68
- resolveAll: adminProcedure
69
- .output(
70
- z.array(
71
- z.object({
72
- nodeId: z.string(),
73
- resolution: HwAccelResolutionSchema,
74
- }),
75
- ),
76
- )
77
- .query(async () => {
78
- if (!broker) throw new Error('Moleculer broker not available')
79
- const registry = (
80
- broker as unknown as {
81
- registry: {
82
- getNodeList: (opts: { onlyAvailable: boolean }) => readonly { id: string }[]
83
- }
84
- }
85
- ).registry
86
- const nodes = registry.getNodeList({ onlyAvailable: true })
87
- const results = await Promise.all(
88
- nodes.map(async (n) => {
89
- try {
90
- const resolution = (await broker.call(
91
- '$hwaccel.resolve',
92
- { prefer: null },
93
- { nodeID: n.id },
94
- )) as HwAccelResolution
95
- return { nodeId: n.id, resolution }
96
- } catch (err) {
97
- const msg = err instanceof Error ? err.message : String(err)
98
- return {
99
- nodeId: n.id,
100
- resolution: { preferred: [] as const, rationale: `resolve failed: ${msg}` },
101
- }
102
- }
103
- }),
104
- )
105
- return results
106
- }),
107
- })
108
- }
@@ -1,67 +0,0 @@
1
- /**
2
- * Live events router — fixed core API (not a capability).
3
- *
4
- * Higher-level live-subscription API consumed by the admin UI dashboard.
5
- * Wraps `EventBusService` with per-device permission enforcement.
6
- */
7
- import { z } from 'zod'
8
- import type { EventBusService } from '../../core/events/event-bus.service.js'
9
- import type { AddonRegistryService } from '../../core/addon/addon-registry.service.js'
10
- import { trpcRouter, protectedProcedure, iterableSubscription } from '../trpc/trpc.middleware.js'
11
-
12
- export function createLiveEventsRouter(eb: EventBusService, ar: AddonRegistryService) {
13
- return trpcRouter({
14
- recentSystemEvents: protectedProcedure
15
- .input(
16
- z
17
- .object({
18
- category: z.string().optional(),
19
- limit: z.number().optional(),
20
- })
21
- .optional(),
22
- )
23
- .query(({ input }) => eb.getRecent(input ?? {}, input?.limit ?? 50)),
24
-
25
- onEvent: protectedProcedure
26
- .input(z.object({ category: z.string().optional() }))
27
- .subscription(({ input }) => {
28
- return iterableSubscription<{
29
- id: string
30
- timestamp: Date
31
- source: { type: string; id: string | number }
32
- category: string
33
- data: Record<string, unknown>
34
- }>((push) => {
35
- const filter: Record<string, unknown> = {}
36
- if (input.category) filter.category = input.category
37
- return eb.subscribe(filter, push)
38
- })
39
- }),
40
-
41
- onDeviceEvent: protectedProcedure
42
- .input(z.object({ deviceId: z.number() }))
43
- .subscription(({ input, ctx }) => {
44
- return iterableSubscription<unknown>((push) => {
45
- const deviceRegistry = ar.getDeviceRegistry()
46
- const device = deviceRegistry.getById(input.deviceId)
47
- if (!device) throw new Error(`Device id=${input.deviceId} not found`)
48
- // Permission check — new IDevice has no providerId; admin bypass only
49
- if (!ctx.user.isAdmin) {
50
- const ad = ctx.user.permissions?.allowedDevices
51
- if (!ad) throw new Error('Access denied')
52
- // Find which addon owns this device to determine the provider key
53
- const ownerAddonId = deviceRegistry.getAddonId(input.deviceId)
54
- if (!ownerAddonId) throw new Error('Access denied')
55
- const pd = ad[ownerAddonId]
56
- // Permission lists are still keyed by stableId — that's the
57
- // external admin-facing identifier. Resolve the device's
58
- // stableId for the membership check.
59
- if (!pd || (pd !== '*' && !pd.includes(device.stableId))) {
60
- throw new Error('Access denied')
61
- }
62
- }
63
- return eb.subscribe({ source: { type: 'device', id: input.deviceId } }, push)
64
- })
65
- }),
66
- })
67
- }
@@ -1,195 +0,0 @@
1
- /**
2
- * Logs router — fixed core API (not a capability).
3
- *
4
- * Queries and streams log entries via `LoggingService` (thin NestJS DI
5
- * wrapper over `LogManager` from `@camstack/core`). Admin can read
6
- * everything; viewers/protected users only see entries matching their
7
- * allowed providers scope (filtered in `query`).
8
- */
9
- import { z } from 'zod'
10
- import type { LoggingService } from '../../core/logging/logging.service.js'
11
- import {
12
- trpcRouter,
13
- protectedProcedure,
14
- adminProcedure,
15
- iterableSubscription,
16
- } from '../trpc/trpc.middleware.js'
17
- import type { LogEntry } from '@camstack/types'
18
-
19
- const LogLevelSchema = z.enum(['debug', 'info', 'warn', 'error'])
20
-
21
- const LogTagsSchema = z.object({
22
- agentId: z.string().optional(),
23
- nodeId: z.string().optional(),
24
- /** Numeric progressive id (or its string form for legacy callers). */
25
- deviceId: z.union([z.string(), z.number()]).optional(),
26
- /** Parent container id — set on every accessory child's logs (and on the
27
- * container's own logs). Filtering by it returns the whole container subtree
28
- * (container + all children) in one query. */
29
- containerDeviceId: z.union([z.string(), z.number()]).optional(),
30
- deviceName: z.string().optional(),
31
- integrationId: z.string().optional(),
32
- addonId: z.string().optional(),
33
- streamId: z.string().optional(),
34
- streamName: z.string().optional(),
35
- sessionId: z.string().optional(),
36
- /**
37
- * Correlation id used to scope a single short-lived operation
38
- * (e.g. a `testCreationField` probe). The Add-Device modal generates
39
- * one per dialog open and providers tag their loggers with it so the
40
- * modal can stream test logs without a deviceId (the device doesn't
41
- * exist yet).
42
- */
43
- requestId: z.string().optional(),
44
- })
45
-
46
- /** Wire shape of a log entry — timestamp is serialised to ISO string by tRPC. */
47
- const LogEntrySchema = z.object({
48
- timestamp: z.string(),
49
- level: z.string(),
50
- scope: z.string().optional(),
51
- message: z.string(),
52
- tags: z.record(z.string(), z.string().optional()).optional(),
53
- /**
54
- * Ad-hoc structured payload attached to the log call (e.g.
55
- * `{error, brokerId, sourceType, url}` for stream-broker errors).
56
- * Tags are filterable structural fields; meta is the per-call
57
- * payload operators read in the expanded LogStream row.
58
- */
59
- meta: z.record(z.string(), z.unknown()).optional(),
60
- })
61
-
62
- const LogEntryArraySchema = z.array(LogEntrySchema)
63
-
64
- export function createLogsRouter(logging: LoggingService) {
65
- return trpcRouter({
66
- query: adminProcedure
67
- .input(
68
- z.object({
69
- level: LogLevelSchema.optional(),
70
- since: z.number().optional(),
71
- until: z.number().optional(),
72
- limit: z.number().optional(),
73
- tags: LogTagsSchema.optional(),
74
- }),
75
- )
76
- .output(LogEntryArraySchema)
77
- .query(({ input, ctx }) => {
78
- const filter: Record<string, unknown> = { limit: input.limit ?? 100 }
79
- if (input.level) filter.level = input.level
80
- if (input.since !== undefined) filter.since = new Date(input.since)
81
- if (input.until !== undefined) filter.until = new Date(input.until)
82
- if (input.tags) filter.tags = input.tags
83
- const entries: LogEntry[] = logging.query(filter)
84
- const filtered = (() => {
85
- if (!ctx.user.isAdmin) {
86
- const ap = ctx.user.permissions?.allowedProviders
87
- if (ap && ap !== '*') {
88
- const allowed = ap as string[]
89
- return entries.filter((e) => {
90
- const addonId = e.tags?.addonId
91
- if (typeof addonId !== 'string') return false
92
- return allowed.some((p) => {
93
- const bare = p.startsWith('addon:') ? p.slice('addon:'.length) : p
94
- return addonId === bare || addonId.startsWith(bare)
95
- })
96
- })
97
- }
98
- }
99
- return entries
100
- })()
101
- return filtered.map((e) => ({
102
- timestamp: e.timestamp instanceof Date ? e.timestamp.toISOString() : String(e.timestamp),
103
- level: e.level,
104
- ...(e.scope !== undefined ? { scope: e.scope } : {}),
105
- message: e.message,
106
- // Tags carry mixed primitive types (numeric deviceId is common —
107
- // see kernel/capability-registry.ts logging calls). The wire
108
- // schema is Record<string,string>, so stringify here. Mirrors
109
- // the same normalisation applied in `subscribe` below.
110
- tags: e.tags
111
- ? Object.fromEntries(
112
- Object.entries(e.tags).map(([k, v]) => [
113
- k,
114
- v === undefined ? undefined : String(v),
115
- ]),
116
- )
117
- : undefined,
118
- ...(e.meta !== undefined ? { meta: e.meta } : {}),
119
- }))
120
- }),
121
-
122
- /**
123
- * Drop matching entries from the in-memory ring buffer. Powers
124
- * the UI's "Clear logs" button — without server-side purge, the
125
- * cleared rows reappear on the next page open from the historical
126
- * `query`. Admin-only so a viewer can't wipe context other ops
127
- * are reading.
128
- */
129
- clear: adminProcedure
130
- .input(
131
- z.object({
132
- level: LogLevelSchema.optional(),
133
- since: z.number().optional(),
134
- until: z.number().optional(),
135
- tags: LogTagsSchema.optional(),
136
- }),
137
- )
138
- .output(z.object({ removed: z.number().int().nonnegative() }))
139
- .mutation(({ input }) => {
140
- const filter: Record<string, unknown> = {}
141
- if (input.level) filter.level = input.level
142
- if (input.since !== undefined) filter.since = new Date(input.since)
143
- if (input.until !== undefined) filter.until = new Date(input.until)
144
- if (input.tags) filter.tags = input.tags
145
- const removed = logging.clear(filter)
146
- return { removed }
147
- }),
148
-
149
- subscribe: protectedProcedure
150
- .input(
151
- z.object({
152
- level: LogLevelSchema.optional(),
153
- tags: LogTagsSchema.optional(),
154
- }),
155
- )
156
- .subscription(({ input }) => {
157
- return iterableSubscription<unknown>((push) => {
158
- return logging.subscribe(
159
- {
160
- level: input.level,
161
- tags: input.tags as Record<string, string> | undefined,
162
- },
163
- (entry: {
164
- timestamp: Date | string | number
165
- level: string
166
- message: string
167
- scope?: string
168
- tags?: Record<string, string | number | undefined>
169
- meta?: Record<string, unknown>
170
- }) => {
171
- const stringifiedTags = entry.tags
172
- ? Object.fromEntries(
173
- Object.entries(entry.tags).map(([k, v]) => [
174
- k,
175
- v === undefined ? undefined : String(v),
176
- ]),
177
- )
178
- : undefined
179
- push({
180
- timestamp:
181
- entry.timestamp instanceof Date
182
- ? entry.timestamp.toISOString()
183
- : String(entry.timestamp),
184
- level: entry.level,
185
- message: entry.message,
186
- scope: entry.scope,
187
- tags: stringifiedTags,
188
- meta: entry.meta,
189
- })
190
- },
191
- )
192
- })
193
- }),
194
- })
195
- }
@@ -1,66 +0,0 @@
1
- /**
2
- * Notifications router — fixed core API (not a capability).
3
- *
4
- * Routes events by category to registered outputs. The NotificationService
5
- * is a singleton from @camstack/core; individual outputs are still provided
6
- * by addons via the `notification-output` capability collection.
7
- */
8
- import { z } from 'zod'
9
- import type { NotificationService } from '@camstack/core'
10
- import { trpcRouter, protectedProcedure, adminProcedure } from '../trpc/trpc.middleware.js'
11
-
12
- const NotificationOutputSchema = z.object({
13
- id: z.string(),
14
- name: z.string(),
15
- icon: z.string(),
16
- })
17
-
18
- const SendTestResultSchema = z.object({
19
- success: z.boolean(),
20
- error: z.string().optional(),
21
- })
22
-
23
- export function createNotificationsRouter(ns: NotificationService | null) {
24
- return trpcRouter({
25
- listOutputs: protectedProcedure
26
- .input(z.void())
27
- .output(z.array(NotificationOutputSchema).readonly())
28
- .query(() => {
29
- if (!ns) return []
30
- return ns.getOutputs()
31
- }),
32
-
33
- getRouting: protectedProcedure
34
- .input(z.void())
35
- .output(z.record(z.string(), z.array(z.string())))
36
- .query(() => {
37
- if (!ns) return {}
38
- const routing = ns.getRouting()
39
- const result: Record<string, string[]> = {}
40
- for (const [category, outputIds] of routing) {
41
- result[category] = [...outputIds]
42
- }
43
- return result
44
- }),
45
-
46
- setRouting: adminProcedure
47
- .input(z.object({ category: z.string(), outputIds: z.array(z.string()) }))
48
- .output(z.void())
49
- .mutation(({ input }) => {
50
- if (!ns) throw new Error('Notification service unavailable')
51
- ns.setRouting(input.category, input.outputIds)
52
- }),
53
-
54
- sendTest: adminProcedure
55
- .input(z.object({ outputId: z.string() }))
56
- .output(SendTestResultSchema)
57
- .mutation(async ({ input }) => {
58
- if (!ns) return { success: false, error: 'Notification service unavailable' }
59
- const output = ns.getOutput(input.outputId)
60
- if (!output) return { success: false, error: `Output "${input.outputId}" not found` }
61
- if (!output.sendTest)
62
- return { success: false, error: 'Output does not support test notifications' }
63
- return output.sendTest()
64
- }),
65
- })
66
- }