@camstack/server 0.1.3

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 (133) hide show
  1. package/.env.example +17 -0
  2. package/package.json +55 -0
  3. package/src/__tests__/addon-install-e2e.test.ts +75 -0
  4. package/src/__tests__/addon-pages-e2e.test.ts +178 -0
  5. package/src/__tests__/addon-route-session.test.ts +17 -0
  6. package/src/__tests__/addon-settings-router.spec.ts +62 -0
  7. package/src/__tests__/addon-upload.spec.ts +355 -0
  8. package/src/__tests__/agent-registry.spec.ts +162 -0
  9. package/src/__tests__/agent-status-page.spec.ts +84 -0
  10. package/src/__tests__/auth-session-cookie.test.ts +21 -0
  11. package/src/__tests__/cap-providers/cap-usage-graph.spec.ts +23 -0
  12. package/src/__tests__/cap-providers/compute-topology-categories.spec.ts +64 -0
  13. package/src/__tests__/cap-routers/_meta.spec.ts +200 -0
  14. package/src/__tests__/cap-routers/addon-settings.router.spec.ts +106 -0
  15. package/src/__tests__/cap-routers/device-manager-aggregate.router.spec.ts +142 -0
  16. package/src/__tests__/cap-routers/harness.ts +159 -0
  17. package/src/__tests__/cap-routers/metrics-provider.router.spec.ts +119 -0
  18. package/src/__tests__/cap-routers/null-provider-guard.spec.ts +66 -0
  19. package/src/__tests__/cap-routers/pipeline-executor.router.spec.ts +135 -0
  20. package/src/__tests__/cap-routers/settings-store.router.spec.ts +247 -0
  21. package/src/__tests__/capability-e2e.test.ts +386 -0
  22. package/src/__tests__/cli-e2e.test.ts +129 -0
  23. package/src/__tests__/core-cap-bridge.spec.ts +89 -0
  24. package/src/__tests__/embedded-deps-e2e.test.ts +109 -0
  25. package/src/__tests__/event-bus-proxy-router.spec.ts +72 -0
  26. package/src/__tests__/fixtures/mock-analysis-addon-a.ts +37 -0
  27. package/src/__tests__/fixtures/mock-analysis-addon-b.ts +37 -0
  28. package/src/__tests__/fixtures/mock-log-addon.ts +37 -0
  29. package/src/__tests__/fixtures/mock-storage-addon.ts +40 -0
  30. package/src/__tests__/framework-allowlist.spec.ts +95 -0
  31. package/src/__tests__/https-e2e.test.ts +118 -0
  32. package/src/__tests__/lifecycle-e2e.test.ts +140 -0
  33. package/src/__tests__/live-events-subscription.spec.ts +150 -0
  34. package/src/__tests__/moleculer-register-node-idempotency.spec.ts +229 -0
  35. package/src/__tests__/oauth2-account-linking.spec.ts +736 -0
  36. package/src/__tests__/post-boot-restart.spec.ts +161 -0
  37. package/src/__tests__/singleton-contention.test.ts +487 -0
  38. package/src/__tests__/streaming-diagnostic.test.ts +512 -0
  39. package/src/__tests__/streaming-scale.test.ts +280 -0
  40. package/src/agent-status-page.ts +121 -0
  41. package/src/api/__tests__/addons-custom.spec.ts +134 -0
  42. package/src/api/__tests__/capabilities.router.test.ts +47 -0
  43. package/src/api/addon-upload.ts +472 -0
  44. package/src/api/addons-custom.router.ts +100 -0
  45. package/src/api/auth-whoami.ts +99 -0
  46. package/src/api/bridge-addons.router.ts +120 -0
  47. package/src/api/capabilities.router.ts +226 -0
  48. package/src/api/core/__tests__/auth-router-totp.spec.ts +256 -0
  49. package/src/api/core/addon-settings.router.ts +124 -0
  50. package/src/api/core/agents.router.ts +87 -0
  51. package/src/api/core/auth.router.ts +303 -0
  52. package/src/api/core/cap-providers.ts +993 -0
  53. package/src/api/core/capabilities.router.ts +119 -0
  54. package/src/api/core/collection-preference.ts +40 -0
  55. package/src/api/core/event-bus-proxy.router.ts +45 -0
  56. package/src/api/core/hwaccel.router.ts +81 -0
  57. package/src/api/core/live-events.router.ts +60 -0
  58. package/src/api/core/logs.router.ts +162 -0
  59. package/src/api/core/notifications.router.ts +65 -0
  60. package/src/api/core/repl.router.ts +41 -0
  61. package/src/api/core/settings-backend.router.ts +142 -0
  62. package/src/api/core/stream-probe.router.ts +57 -0
  63. package/src/api/core/system-events.router.ts +116 -0
  64. package/src/api/health/health.routes.ts +123 -0
  65. package/src/api/oauth2/__tests__/oauth2-routes.spec.ts +52 -0
  66. package/src/api/oauth2/consent-page.ts +42 -0
  67. package/src/api/oauth2/oauth2-routes.ts +248 -0
  68. package/src/api/trpc/__tests__/scope-access-device.spec.ts +223 -0
  69. package/src/api/trpc/__tests__/scope-access.spec.ts +107 -0
  70. package/src/api/trpc/cap-mount-helpers.ts +225 -0
  71. package/src/api/trpc/core-cap-bridge.ts +152 -0
  72. package/src/api/trpc/generated-cap-mounts.ts +707 -0
  73. package/src/api/trpc/generated-cap-routers.ts +6340 -0
  74. package/src/api/trpc/scope-access.ts +110 -0
  75. package/src/api/trpc/trpc.context.ts +255 -0
  76. package/src/api/trpc/trpc.middleware.ts +140 -0
  77. package/src/api/trpc/trpc.router.ts +275 -0
  78. package/src/auth/session-cookie.ts +44 -0
  79. package/src/boot/boot-config.ts +278 -0
  80. package/src/boot/post-boot.service.ts +103 -0
  81. package/src/core/addon/__tests__/addon-registry-capability.test.ts +53 -0
  82. package/src/core/addon/addon-package.service.ts +1684 -0
  83. package/src/core/addon/addon-registry.service.ts +2926 -0
  84. package/src/core/addon/addon-search.service.ts +90 -0
  85. package/src/core/addon/addon-settings-provider.ts +276 -0
  86. package/src/core/addon/addon.tokens.ts +2 -0
  87. package/src/core/addon-bridge/addon-bridge.service.ts +125 -0
  88. package/src/core/addon-pages/addon-pages.service.spec.ts +117 -0
  89. package/src/core/addon-pages/addon-pages.service.ts +80 -0
  90. package/src/core/addon-widgets/addon-widgets.service.ts +92 -0
  91. package/src/core/agent/agent-registry.service.ts +507 -0
  92. package/src/core/auth/auth.service.spec.ts +88 -0
  93. package/src/core/auth/auth.service.ts +8 -0
  94. package/src/core/capability/capability.service.ts +57 -0
  95. package/src/core/config/config.schema.ts +3 -0
  96. package/src/core/config/config.service.spec.ts +175 -0
  97. package/src/core/config/config.service.ts +7 -0
  98. package/src/core/events/event-bus.service.spec.ts +212 -0
  99. package/src/core/events/event-bus.service.ts +85 -0
  100. package/src/core/feature/feature.service.spec.ts +96 -0
  101. package/src/core/feature/feature.service.ts +8 -0
  102. package/src/core/lifecycle/lifecycle-state-machine.spec.ts +168 -0
  103. package/src/core/lifecycle/lifecycle-state-machine.ts +3 -0
  104. package/src/core/logging/log-ring-buffer.ts +3 -0
  105. package/src/core/logging/logging.service.spec.ts +247 -0
  106. package/src/core/logging/logging.service.ts +129 -0
  107. package/src/core/logging/scoped-logger.ts +3 -0
  108. package/src/core/moleculer/moleculer.service.ts +612 -0
  109. package/src/core/network/network-quality.service.spec.ts +47 -0
  110. package/src/core/network/network-quality.service.ts +5 -0
  111. package/src/core/notification/notification-wrapper.service.ts +36 -0
  112. package/src/core/notification/toast-wrapper.service.ts +31 -0
  113. package/src/core/provider/provider.tokens.ts +1 -0
  114. package/src/core/repl/repl-engine.service.spec.ts +417 -0
  115. package/src/core/repl/repl-engine.service.ts +156 -0
  116. package/src/core/storage/fs-storage-backend.spec.ts +70 -0
  117. package/src/core/storage/fs-storage-backend.ts +3 -0
  118. package/src/core/storage/settings-store.spec.ts +213 -0
  119. package/src/core/storage/settings-store.ts +2 -0
  120. package/src/core/storage/sql-schema.spec.ts +140 -0
  121. package/src/core/storage/sql-schema.ts +3 -0
  122. package/src/core/storage/storage-location-manager.spec.ts +121 -0
  123. package/src/core/storage/storage-location-manager.ts +3 -0
  124. package/src/core/storage/storage.service.spec.ts +73 -0
  125. package/src/core/storage/storage.service.ts +3 -0
  126. package/src/core/streaming/stream-probe.service.ts +212 -0
  127. package/src/core/topology/topology-emitter.service.ts +101 -0
  128. package/src/launcher.ts +309 -0
  129. package/src/main.ts +1049 -0
  130. package/src/manual-boot.ts +322 -0
  131. package/tsconfig.build.json +8 -0
  132. package/tsconfig.json +21 -0
  133. package/vitest.config.ts +26 -0
@@ -0,0 +1,103 @@
1
+ import { randomUUID } from 'node:crypto'
2
+ import { readPendingRestart } from '@camstack/kernel'
3
+ import type { IScopedLogger, PendingRestartMarkerPayload } from '@camstack/types'
4
+ import { AddonRegistryService } from '../core/addon/addon-registry.service'
5
+ import { EventBusService } from '../core/events/event-bus.service'
6
+ import { LoggingService } from '../core/logging/logging.service'
7
+
8
+ export interface PostBootContext {
9
+ readonly port: number
10
+ readonly host: string
11
+ readonly dataPath: string
12
+ readonly trpcRegistered: boolean
13
+ }
14
+
15
+ export class PostBootService {
16
+ private readonly logger: IScopedLogger
17
+
18
+ /**
19
+ * Holds the marker that fired `system.restart-completed` on this boot
20
+ * for `LAST_RESTART_RETENTION_MS`. Lets clients query the marker
21
+ * after they reconnect — the live event itself is emitted before the
22
+ * WS resubscribe has a chance to land.
23
+ */
24
+ private static lastRestart: { payload: PendingRestartMarkerPayload; expiresAt: number } | null = null
25
+
26
+ /** How long the last-restart marker stays queryable after boot (5 min). */
27
+ static readonly LAST_RESTART_RETENTION_MS = 5 * 60_000
28
+
29
+ constructor(
30
+ _addonRegistry: AddonRegistryService,
31
+ private readonly eventBus: EventBusService,
32
+ loggingService: LoggingService,
33
+ ) {
34
+ this.logger = loggingService.createLogger('PostBoot')
35
+ }
36
+
37
+ /**
38
+ * Snapshot of the most-recent restart marker, or `null` after the
39
+ * retention window. The hub's `addons.getLastRestart` cap method
40
+ * delegates here.
41
+ */
42
+ static getLastRestart(): PendingRestartMarkerPayload | null {
43
+ const entry = PostBootService.lastRestart
44
+ if (entry === null) return null
45
+ if (entry.expiresAt <= Date.now()) {
46
+ PostBootService.lastRestart = null
47
+ return null
48
+ }
49
+ return entry.payload
50
+ }
51
+
52
+ async run(context: PostBootContext): Promise<void> {
53
+ const { port, host, dataPath, trpcRegistered } = context
54
+
55
+ // Device stream wiring moved into `addon-stream-broker` — the addon
56
+ // does its own initial sync against `ctx.deviceRegistry` plus listens
57
+ // to `DeviceRegistered` events for future registrations. No kernel
58
+ // orchestration shim needed here anymore.
59
+
60
+ // Emit system.boot event
61
+ this.eventBus.emit({
62
+ id: randomUUID(),
63
+ timestamp: new Date(),
64
+ source: { type: 'core', id: 'system' },
65
+ category: 'system.boot',
66
+ data: { port, host, trpcRegistered, dataPath },
67
+ })
68
+
69
+ // If the previous shutdown was driven by RestartCoordinator, emit a
70
+ // `system.restart-completed` event so the admin UI can surface a
71
+ // success toast describing what changed (framework update, manual
72
+ // restart, …). `readPendingRestart` clears the marker atomically so
73
+ // we never re-fire on a crash-loop boot.
74
+ this.emitRestartCompletedIfPending(dataPath)
75
+ }
76
+
77
+ private emitRestartCompletedIfPending(dataDir: string): void {
78
+ const marker = readPendingRestart(dataDir)
79
+ if (marker === null) return
80
+
81
+ const payload: PendingRestartMarkerPayload = {
82
+ kind: marker.kind,
83
+ requestedAt: marker.requestedAt,
84
+ ...(marker.packageName !== undefined ? { packageName: marker.packageName } : {}),
85
+ ...(marker.fromVersion !== undefined ? { fromVersion: marker.fromVersion } : {}),
86
+ ...(marker.toVersion !== undefined ? { toVersion: marker.toVersion } : {}),
87
+ ...(marker.requestedBy !== undefined ? { requestedBy: marker.requestedBy } : {}),
88
+ }
89
+
90
+ this.logger.info('Restart completed', { meta: payload })
91
+ PostBootService.lastRestart = {
92
+ payload,
93
+ expiresAt: Date.now() + PostBootService.LAST_RESTART_RETENTION_MS,
94
+ }
95
+ this.eventBus.emit({
96
+ id: randomUUID(),
97
+ timestamp: new Date(),
98
+ source: { type: 'core', id: 'system' },
99
+ category: 'system.restart-completed',
100
+ data: payload,
101
+ })
102
+ }
103
+ }
@@ -0,0 +1,53 @@
1
+
2
+ // server/backend/src/core/addon/__tests__/addon-registry-capability.test.ts
3
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
4
+ import { CapabilityRegistry } from '@camstack/kernel'
5
+ import type { IScopedLogger } from '@camstack/types'
6
+
7
+ function createMockLogger(): IScopedLogger {
8
+ return {
9
+ error: vi.fn(),
10
+ warn: vi.fn(),
11
+ info: vi.fn(),
12
+ debug: vi.fn(),
13
+ child: vi.fn().mockReturnThis(),
14
+ }
15
+ }
16
+
17
+ describe('AddonRegistryService -- CapabilityRegistry integration', () => {
18
+ let registry: CapabilityRegistry
19
+
20
+ beforeEach(() => {
21
+ registry = new CapabilityRegistry(createMockLogger())
22
+ registry.ready()
23
+ })
24
+
25
+ it('singleton provider is available after registerProvider', () => {
26
+ registry.declareCapability({ name: 'storage', scope: 'system', mode: 'singleton', methods: {} })
27
+
28
+ const mockStorageProvider = { getLocation: vi.fn() }
29
+ registry.registerProvider('storage', 'sqlite-storage', mockStorageProvider)
30
+
31
+ expect(registry.getSingleton('storage')).toBe(mockStorageProvider)
32
+ })
33
+
34
+ it('streaming-engine singleton wired after provider registers', () => {
35
+ registry.declareCapability({ name: 'streaming-engine', scope: 'system', mode: 'singleton', methods: {} })
36
+
37
+ const mockEngine = { initialize: vi.fn() }
38
+ registry.registerProvider('streaming-engine', 'go2rtc', mockEngine)
39
+
40
+ expect(registry.getSingleton('streaming-engine')).toBe(mockEngine)
41
+ })
42
+
43
+ it('log-destination collection receives all providers', () => {
44
+ registry.declareCapability({ name: 'log-destination', scope: 'system', mode: 'collection', methods: {} })
45
+
46
+ const dest1 = { id: 'winston' }
47
+ const dest2 = { id: 'loki' }
48
+ registry.registerProvider('log-destination', 'winston-logging', dest1)
49
+ registry.registerProvider('log-destination', 'loki-logging', dest2)
50
+
51
+ expect(registry.getCollection('log-destination')).toEqual([dest1, dest2])
52
+ })
53
+ })