@camstack/server 0.2.2 → 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,96 +0,0 @@
1
- /**
2
- * Framework package allow-list coherence.
3
- *
4
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
5
- *
6
- * Invariants:
7
- * - Every entry in `FRAMEWORK_PACKAGE_ALLOWLIST` resolves to a workspace
8
- * package whose `package.json` declares `camstack.system: true`.
9
- * - Every workspace package with `camstack.system: true` appears in the
10
- * allow-list — drift in either direction breaks live-update.
11
- * - `isFrameworkPackage` accepts framework names and rejects everything
12
- * else (sanity check on the gate the cap method uses).
13
- */
14
- import { describe, it, expect } from 'vitest'
15
- import * as fs from 'node:fs'
16
- import * as path from 'node:path'
17
- import {
18
- FRAMEWORK_PACKAGE_ALLOWLIST,
19
- isFrameworkPackage,
20
- } from '../core/addon/addon-package.service.js'
21
-
22
- function repoRoot(): string {
23
- return path.resolve(__dirname, '..', '..', '..', '..')
24
- }
25
-
26
- interface PackageJsonView {
27
- readonly name: string
28
- readonly system: boolean
29
- readonly path: string
30
- }
31
-
32
- function readPackageJson(pkgJsonPath: string): PackageJsonView | null {
33
- try {
34
- const raw: unknown = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'))
35
- if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return null
36
- const obj = raw as Record<string, unknown>
37
- const name = obj['name']
38
- if (typeof name !== 'string') return null
39
- const camstack = obj['camstack']
40
- const isSystem =
41
- camstack !== null &&
42
- typeof camstack === 'object' &&
43
- !Array.isArray(camstack) &&
44
- (camstack as Record<string, unknown>)['system'] === true
45
- return { name, system: isSystem, path: pkgJsonPath }
46
- } catch {
47
- return null
48
- }
49
- }
50
-
51
- function discoverWorkspacePackages(): readonly PackageJsonView[] {
52
- const packagesDir = path.join(repoRoot(), 'packages')
53
- if (!fs.existsSync(packagesDir)) return []
54
- const results: PackageJsonView[] = []
55
- for (const entry of fs.readdirSync(packagesDir, { withFileTypes: true })) {
56
- if (!entry.isDirectory()) continue
57
- const pkgJson = path.join(packagesDir, entry.name, 'package.json')
58
- if (!fs.existsSync(pkgJson)) continue
59
- const view = readPackageJson(pkgJson)
60
- if (view !== null) results.push(view)
61
- }
62
- return results
63
- }
64
-
65
- describe('framework package allow-list — manifest parity', () => {
66
- it('every allow-listed package exists in `packages/` with camstack.system: true', () => {
67
- const pkgs = discoverWorkspacePackages()
68
- for (const name of FRAMEWORK_PACKAGE_ALLOWLIST) {
69
- const match = pkgs.find((p) => p.name === name)
70
- expect(match, `expected ${name} in packages/`).toBeDefined()
71
- expect(match?.system, `${name} must declare camstack.system: true`).toBe(true)
72
- }
73
- })
74
-
75
- it('every workspace package with camstack.system: true is in the allow-list', () => {
76
- const systemPkgs = discoverWorkspacePackages().filter((p) => p.system)
77
- const allowSet = new Set(FRAMEWORK_PACKAGE_ALLOWLIST)
78
- const orphans = systemPkgs.filter((p) => !allowSet.has(p.name))
79
- expect(orphans.map((p) => p.name)).toEqual([])
80
- })
81
- })
82
-
83
- describe('isFrameworkPackage', () => {
84
- it('returns true for every allow-listed package', () => {
85
- for (const name of FRAMEWORK_PACKAGE_ALLOWLIST) {
86
- expect(isFrameworkPackage(name)).toBe(true)
87
- }
88
- })
89
-
90
- it('returns false for addons and unknown packages', () => {
91
- expect(isFrameworkPackage('@camstack/addon-stream-broker')).toBe(false)
92
- expect(isFrameworkPackage('left-pad')).toBe(false)
93
- expect(isFrameworkPackage('')).toBe(false)
94
- expect(isFrameworkPackage('@camstack')).toBe(false)
95
- })
96
- })
@@ -1,165 +0,0 @@
1
- /**
2
- * deferRestart flag on AddonPackageService.updateFrameworkPackage
3
- *
4
- * Spec: docs/superpowers/specs/2026-05-21-addons-bulk-update-progress-design.md
5
- *
6
- * Invariants:
7
- * - deferRestart: true → npm install runs normally; marker write AND
8
- * scheduleSelfRestart are SKIPPED; restartingAt is 0 (sentinel).
9
- * - deferRestart omitted (default) → marker write AND scheduleSelfRestart
10
- * both fire; restartingAt > 0.
11
- */
12
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
13
- import * as os from 'node:os'
14
- import * as path from 'node:path'
15
- import * as fs from 'node:fs'
16
- import type { IScopedLogger } from '@camstack/types'
17
-
18
- // ── Module-level mocks ──────────────────────────────────────────────────────
19
- // Must be hoisted before any import that loads the module under test.
20
- // vi.mock is statically hoisted by vitest's transform, so the factories
21
- // run before the module graph is resolved.
22
-
23
- vi.mock('@camstack/kernel', async (importOriginal) => {
24
- const real = await importOriginal<typeof import('@camstack/kernel')>()
25
- return {
26
- ...real,
27
- scheduleSelfRestart: vi.fn(),
28
- writePendingRestart: vi.fn(),
29
- // resolveNpmVersion and resolveFrameworkPackageAppRoot are private module
30
- // functions inside addon-package.service.ts — they are NOT exported from
31
- // @camstack/kernel, so we don't need to stub them here.
32
- }
33
- })
34
-
35
- // Mock node:child_process so no actual `npm install` / `npm view` is executed.
36
- vi.mock('node:child_process', async (importOriginal) => {
37
- const real = await importOriginal<typeof import('node:child_process')>()
38
- return {
39
- ...real,
40
- execFile: vi.fn(
41
- (
42
- _cmd: string,
43
- _args: string[],
44
- _opts: unknown,
45
- callback: (err: null, result: { stdout: string; stderr: string }) => void,
46
- ) => {
47
- // Simulate `npm view @camstack/types@0.1.40 version` → '0.1.40'
48
- callback(null, { stdout: '0.1.40\n', stderr: '' })
49
- return { kill: () => undefined }
50
- },
51
- ),
52
- }
53
- })
54
-
55
- // ── Imports that depend on the mocked modules ───────────────────────────────
56
- import { scheduleSelfRestart, writePendingRestart } from '@camstack/kernel'
57
- import { AddonPackageService } from '../core/addon/addon-package.service.js'
58
- import { EventCategory } from '@camstack/types'
59
-
60
- // ── Helpers ──────────────────────────────────────────────────────────────────
61
-
62
- function makeLogger(): IScopedLogger {
63
- const logger: IScopedLogger = {
64
- info: vi.fn(),
65
- warn: vi.fn(),
66
- error: vi.fn(),
67
- debug: vi.fn(),
68
- trace: vi.fn(),
69
- fatal: vi.fn(),
70
- child: (() => logger) as IScopedLogger['child'],
71
- } as unknown as IScopedLogger
72
- return logger
73
- }
74
-
75
- interface ServiceEnv {
76
- readonly service: AddonPackageService
77
- readonly tmpDir: string
78
- readonly scheduleSelfRestartMock: ReturnType<typeof vi.fn>
79
- readonly writePendingRestartMock: ReturnType<typeof vi.fn>
80
- }
81
-
82
- function createEnv(): ServiceEnv {
83
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'defer-restart-spec-'))
84
-
85
- const logger = makeLogger()
86
-
87
- const loggingService = {
88
- createLogger: () => logger,
89
- }
90
-
91
- const eventBusService = {
92
- emit: vi.fn(),
93
- }
94
-
95
- const configService = {}
96
- const addonRegistry = {}
97
- const notificationService = {}
98
- const toastService = {}
99
-
100
- const service = new AddonPackageService(
101
- loggingService as never,
102
- eventBusService as never,
103
- configService as never,
104
- addonRegistry as never,
105
- notificationService as never,
106
- toastService as never,
107
- )
108
-
109
- // Point the framework-package app-root resolver at our tmp dir so
110
- // `npm install --prefix <appRoot>` (which is mocked anyway) has a
111
- // valid-looking path without requiring the real workspace layout.
112
- process.env['CAMSTACK_FRAMEWORK_APP_ROOT_OVERRIDE'] = tmpDir
113
-
114
- return {
115
- service,
116
- tmpDir,
117
- scheduleSelfRestartMock: scheduleSelfRestart as ReturnType<typeof vi.fn>,
118
- writePendingRestartMock: writePendingRestart as ReturnType<typeof vi.fn>,
119
- }
120
- }
121
-
122
- // ── Tests ────────────────────────────────────────────────────────────────────
123
-
124
- describe('AddonPackageService.updateFrameworkPackage — deferRestart flag', () => {
125
- let env: ServiceEnv
126
-
127
- beforeEach(() => {
128
- vi.clearAllMocks()
129
- env = createEnv()
130
- })
131
-
132
- afterEach(() => {
133
- fs.rmSync(env.tmpDir, { recursive: true, force: true })
134
- delete process.env['CAMSTACK_FRAMEWORK_APP_ROOT_OVERRIDE']
135
- vi.restoreAllMocks()
136
- })
137
-
138
- it('skips restart seam (writePendingRestart + scheduleSelfRestart) when deferRestart is true', async () => {
139
- const result = await env.service.updateFrameworkPackage({
140
- packageName: '@camstack/types',
141
- version: '0.1.40',
142
- deferRestart: true,
143
- })
144
-
145
- expect(env.writePendingRestartMock).not.toHaveBeenCalled()
146
- expect(env.scheduleSelfRestartMock).not.toHaveBeenCalled()
147
- expect(result.packageName).toBe('@camstack/types')
148
- expect(result.toVersion).toBe('0.1.40')
149
- // Sentinel: 0 signals "no restart scheduled"
150
- expect(result.restartingAt).toBe(0)
151
- })
152
-
153
- it('fires writePendingRestart + scheduleSelfRestart when deferRestart is omitted', async () => {
154
- const result = await env.service.updateFrameworkPackage({
155
- packageName: '@camstack/types',
156
- version: '0.1.40',
157
- })
158
-
159
- expect(env.writePendingRestartMock).toHaveBeenCalledOnce()
160
- expect(env.scheduleSelfRestartMock).toHaveBeenCalledOnce()
161
- expect(result.packageName).toBe('@camstack/types')
162
- expect(result.toVersion).toBe('0.1.40')
163
- expect(result.restartingAt).toBeGreaterThan(0)
164
- })
165
- })
@@ -1,124 +0,0 @@
1
- /**
2
- * HTTPS E2E tests — verify self-signed cert generation, HTTPS serving, and WSS agent connection.
3
- */
4
- import { describe, it, expect, beforeAll, afterAll } from 'vitest'
5
- import * as fs from 'node:fs'
6
- import * as path from 'node:path'
7
- import * as os from 'node:os'
8
- import * as https from 'node:https'
9
- import { X509Certificate } from 'node:crypto'
10
- // Import directly from source submodule because vitest+swc doesn't resolve
11
- // `export * from './tls/index.js'` barrel re-exports in the @camstack/core index.
12
- import { ensureTlsCert, loadTlsCert } from '../../../../packages/core/src/tls/cert-manager'
13
-
14
- describe('HTTPS E2E', () => {
15
- let tmpDir: string
16
-
17
- beforeAll(() => {
18
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'camstack-https-e2e-'))
19
- })
20
-
21
- afterAll(() => {
22
- fs.rmSync(tmpDir, { recursive: true, force: true })
23
- })
24
-
25
- it('generates a valid self-signed cert on first call', async () => {
26
- const result = await ensureTlsCert(tmpDir)
27
- expect(result.generated).toBe(true)
28
-
29
- // Verify cert file is valid PEM
30
- const certPem = fs.readFileSync(result.certPath, 'utf-8')
31
- expect(certPem).toContain('-----BEGIN CERTIFICATE-----')
32
-
33
- // Parse and validate
34
- const x509 = new X509Certificate(certPem)
35
- expect(x509.subject).toContain('CN=camstack.local')
36
-
37
- // Check SAN includes localhost
38
- const san = x509.subjectAltName ?? ''
39
- expect(san).toContain('DNS:localhost')
40
- expect(san).toContain('IP Address:127.0.0.1')
41
-
42
- // Check validity (at least 1 year)
43
- const validTo = new Date(x509.validTo)
44
- const oneYear = new Date()
45
- oneYear.setFullYear(oneYear.getFullYear() + 1)
46
- expect(validTo.getTime()).toBeGreaterThan(oneYear.getTime() - 86400000)
47
- })
48
-
49
- it('reuses existing cert on subsequent calls', async () => {
50
- const first = await ensureTlsCert(tmpDir)
51
- const second = await ensureTlsCert(tmpDir)
52
- expect(second.generated).toBe(false)
53
-
54
- const cert1 = fs.readFileSync(first.certPath, 'utf-8')
55
- const cert2 = fs.readFileSync(second.certPath, 'utf-8')
56
- expect(cert1).toBe(cert2)
57
- })
58
-
59
- it('serves HTTPS with the generated cert', async () => {
60
- const { certPath, keyPath } = await ensureTlsCert(tmpDir)
61
- const { cert, key } = loadTlsCert(certPath, keyPath)
62
-
63
- const server = https.createServer({ cert, key }, (_req, res) => {
64
- res.writeHead(200, { 'Content-Type': 'text/plain' })
65
- res.end('camstack-ok')
66
- })
67
-
68
- const port = 10000 + Math.floor(Math.random() * 50000)
69
- await new Promise<void>((resolve) => server.listen(port, '127.0.0.1', resolve))
70
-
71
- try {
72
- const body = await new Promise<string>((resolve, reject) => {
73
- const req = https.request(
74
- { hostname: '127.0.0.1', port, path: '/', method: 'GET', rejectUnauthorized: false },
75
- (res) => {
76
- let data = ''
77
- res.on('data', (c) => {
78
- data += c
79
- })
80
- res.on('end', () => resolve(data))
81
- },
82
- )
83
- req.on('error', reject)
84
- req.end()
85
- })
86
-
87
- expect(body).toBe('camstack-ok')
88
- } finally {
89
- server.close()
90
- }
91
- })
92
-
93
- it('TLS disabled falls back to HTTP', async () => {
94
- // When tls.enabled = false, server should work over plain HTTP
95
- // This is a config-level test, verified by checking that FastifyAdapter
96
- // receives no https options when tls is disabled
97
- const http = await import('node:http')
98
- const server = http.createServer((_req, res) => {
99
- res.writeHead(200)
100
- res.end('http-ok')
101
- })
102
-
103
- const port = 10000 + Math.floor(Math.random() * 50000)
104
- await new Promise<void>((resolve) => server.listen(port, '127.0.0.1', resolve))
105
-
106
- try {
107
- const body = await new Promise<string>((resolve, reject) => {
108
- http
109
- .get(`http://127.0.0.1:${port}/`, (res) => {
110
- let data = ''
111
- res.on('data', (c) => {
112
- data += c
113
- })
114
- res.on('end', () => resolve(data))
115
- })
116
- .on('error', reject)
117
- })
118
-
119
- expect(body).toBe('http-ok')
120
- } finally {
121
- server.close()
122
- }
123
- })
124
- })
@@ -1,189 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest'
2
- import { CapabilityRegistry, isInfraCapability } from '@camstack/kernel'
3
- import type { IScopedLogger } from '@camstack/types'
4
-
5
- // ─── Mock helpers ────────────────────────────────────────────────────
6
- //
7
- // PipelineOrchestrator phase-transition + result-flow tests used to live
8
- // in this file (importing from the now-deleted server/backend/src/core/
9
- // orchestrator/ directory). They have been migrated into the runner package
10
- // (`packages/addon-pipeline-runner/src/__tests__/runner.spec.ts`) which
11
- // is the canonical home of the scheduler core.
12
-
13
- function createMockLogger(): IScopedLogger {
14
- return {
15
- info: vi.fn(),
16
- warn: vi.fn(),
17
- error: vi.fn(),
18
- debug: vi.fn(),
19
- } as unknown as IScopedLogger
20
- }
21
-
22
- function createRegistry(configReader?: (cap: string) => string | undefined): CapabilityRegistry {
23
- const registry = new CapabilityRegistry(createMockLogger())
24
- if (configReader) {
25
- registry.setConfigReader(configReader)
26
- }
27
- registry.ready()
28
- return registry
29
- }
30
-
31
- // ─── Tests ────────────────────────────────────────────────────────────
32
-
33
- describe('Full Lifecycle E2E (mock, no external services)', () => {
34
- describe('CapabilityRegistry boot sequence', () => {
35
- it('registers singleton and collection capabilities correctly', () => {
36
- const registry = createRegistry()
37
-
38
- registry.declareCapability({
39
- name: 'storage',
40
- scope: 'system',
41
- mode: 'singleton',
42
- methods: {},
43
- })
44
- registry.declareCapability({
45
- name: 'log-destination',
46
- scope: 'system',
47
- mode: 'collection',
48
- methods: {},
49
- })
50
- registry.declareCapability({
51
- name: 'streaming-engine',
52
- scope: 'system',
53
- mode: 'singleton',
54
- methods: {},
55
- })
56
- registry.declareCapability({
57
- name: 'analysis-pipeline',
58
- scope: 'system',
59
- mode: 'singleton',
60
- methods: {},
61
- })
62
- registry.declareCapability({
63
- name: 'device-provider',
64
- scope: 'system',
65
- mode: 'collection',
66
- methods: {},
67
- })
68
- registry.declareCapability({
69
- name: 'admin-ui',
70
- scope: 'system',
71
- mode: 'singleton',
72
- methods: {},
73
- })
74
-
75
- const mockStorage = { id: 'sqlite' }
76
- const mockLogger = { id: 'winston' }
77
- const mockStreaming = { id: 'go2rtc' }
78
- const mockAnalysis = { id: 'pipeline-analysis' }
79
- const mockProvider = { id: 'frigate' }
80
-
81
- registry.registerProvider('storage', 'sqlite-storage', mockStorage)
82
- registry.registerProvider('log-destination', 'winston-logging', mockLogger)
83
- registry.registerProvider('streaming-engine', 'go2rtc', mockStreaming)
84
- registry.registerProvider('analysis-pipeline', 'pipeline-analysis', mockAnalysis)
85
- registry.registerProvider('device-provider', 'provider-frigate', mockProvider)
86
-
87
- expect(registry.getSingleton('storage')).toBe(mockStorage)
88
- expect(registry.getCollection('log-destination')).toEqual([mockLogger])
89
- expect(registry.getSingleton('streaming-engine')).toBe(mockStreaming)
90
- expect(registry.getSingleton('analysis-pipeline')).toBe(mockAnalysis)
91
- expect(registry.getCollection('device-provider')).toEqual([mockProvider])
92
-
93
- const caps = registry.listCapabilities()
94
- expect(caps.length).toBeGreaterThanOrEqual(5)
95
- })
96
-
97
- it('infra capabilities are identified correctly', () => {
98
- expect(isInfraCapability('storage')).toBe(true)
99
- expect(isInfraCapability('log-destination')).toBe(true)
100
- expect(isInfraCapability('streaming-engine')).toBe(false)
101
- })
102
- })
103
-
104
- // Note: PipelineOrchestrator phase transition + detection result flow tests
105
- // moved to packages/addon-pipeline-runner/src/__tests__/runner.spec.ts
106
- // when the scheduler was extracted into the addon-pipeline-runner package.
107
-
108
- describe('Singleton swap', () => {
109
- it('swapping analysis provider changes getSingleton result', async () => {
110
- const registry = createRegistry()
111
- registry.declareCapability({
112
- name: 'analysis-pipeline',
113
- scope: 'system',
114
- mode: 'singleton',
115
- methods: {},
116
- })
117
-
118
- const analysisA = { id: 'analysis-a', processFrame: vi.fn() }
119
- const analysisB = { id: 'analysis-b', processFrame: vi.fn() }
120
-
121
- registry.registerProvider('analysis-pipeline', 'addon-a', analysisA)
122
- expect(registry.getSingleton('analysis-pipeline')).toBe(analysisA)
123
-
124
- registry.registerProvider('analysis-pipeline', 'addon-b', analysisB)
125
-
126
- await registry.setActiveSingleton('analysis-pipeline', 'addon-b', true)
127
- expect(registry.getSingleton('analysis-pipeline')).toBe(analysisB)
128
- })
129
- })
130
-
131
- describe('Collection capability add/remove', () => {
132
- it('adding and removing device providers', () => {
133
- const registry = createRegistry()
134
- registry.declareCapability({
135
- name: 'device-provider',
136
- scope: 'system',
137
- mode: 'collection',
138
- methods: {},
139
- })
140
-
141
- const frigate = { id: 'frigate', type: 'frigate' }
142
- const onvif = { id: 'onvif', type: 'onvif' }
143
-
144
- registry.registerProvider('device-provider', 'addon-frigate', frigate)
145
- expect(registry.getCollection('device-provider')).toHaveLength(1)
146
-
147
- registry.registerProvider('device-provider', 'addon-onvif', onvif)
148
- expect(registry.getCollection('device-provider')).toHaveLength(2)
149
-
150
- registry.unregisterProvider('device-provider', 'addon-frigate')
151
- expect(registry.getCollection('device-provider')).toHaveLength(1)
152
- })
153
- })
154
-
155
- describe('Capability introspection', () => {
156
- it('listCapabilities returns full info', () => {
157
- const registry = createRegistry()
158
- registry.declareCapability({
159
- name: 'storage',
160
- scope: 'system',
161
- mode: 'singleton',
162
- methods: {},
163
- })
164
- registry.declareCapability({
165
- name: 'device-provider',
166
- scope: 'system',
167
- mode: 'collection',
168
- methods: {},
169
- })
170
-
171
- registry.registerProvider('storage', 'sqlite', { id: 'sqlite' })
172
- registry.registerProvider('device-provider', 'frigate', { id: 'frigate' })
173
- registry.registerProvider('device-provider', 'onvif', { id: 'onvif' })
174
-
175
- const caps = registry.listCapabilities()
176
-
177
- const storage = caps.find((c) => c.name === 'storage')
178
- expect(storage).toBeDefined()
179
- expect(storage!.mode).toBe('singleton')
180
- expect(storage!.providers).toContain('sqlite')
181
- expect(storage!.activeProvider).toBe('sqlite')
182
-
183
- const providers = caps.find((c) => c.name === 'device-provider')
184
- expect(providers).toBeDefined()
185
- expect(providers!.mode).toBe('collection')
186
- expect(providers!.providers).toHaveLength(2)
187
- })
188
- })
189
- })