@camstack/server 0.1.8 → 0.2.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 (125) hide show
  1. package/package.json +9 -7
  2. package/src/__tests__/addon-install-e2e.test.ts +0 -1
  3. package/src/__tests__/addon-pages-e2e.test.ts +40 -18
  4. package/src/__tests__/addon-settings-router.spec.ts +6 -1
  5. package/src/__tests__/addon-upload.spec.ts +91 -29
  6. package/src/__tests__/agent-registry.spec.ts +26 -9
  7. package/src/__tests__/agent-status-page.spec.ts +1 -3
  8. package/src/__tests__/auth-session-cookie.test.ts +28 -1
  9. package/src/__tests__/bulk-update-coordinator.spec.ts +48 -31
  10. package/src/__tests__/cap-ownership-authority.spec.ts +39 -8
  11. package/src/__tests__/cap-providers/cap-providers-location-import.spec.ts +24 -4
  12. package/src/__tests__/cap-providers/cap-usage-graph.spec.ts +17 -3
  13. package/src/__tests__/cap-providers/compute-topology-categories.spec.ts +57 -11
  14. package/src/__tests__/cap-providers/integrations-delete-cascade.spec.ts +64 -15
  15. package/src/__tests__/cap-providers-bulk-update.spec.ts +27 -7
  16. package/src/__tests__/cap-route-adapter.spec.ts +28 -15
  17. package/src/__tests__/cap-routers/_meta.spec.ts +6 -7
  18. package/src/__tests__/cap-routers/addon-settings.router.spec.ts +19 -10
  19. package/src/__tests__/cap-routers/broker-routing.router.spec.ts +14 -6
  20. package/src/__tests__/cap-routers/cap-route-error-formatter.spec.ts +3 -1
  21. package/src/__tests__/cap-routers/capabilities-node.spec.ts +18 -5
  22. package/src/__tests__/cap-routers/device-link-overlay.spec.ts +11 -6
  23. package/src/__tests__/cap-routers/device-manager-aggregate.router.spec.ts +72 -20
  24. package/src/__tests__/cap-routers/harness.ts +11 -7
  25. package/src/__tests__/cap-routers/metrics-provider.router.spec.ts +17 -3
  26. package/src/__tests__/cap-routers/null-provider-guard.spec.ts +5 -7
  27. package/src/__tests__/cap-routers/pipeline-executor.router.spec.ts +35 -11
  28. package/src/__tests__/cap-routers/settings-store.router.spec.ts +59 -15
  29. package/src/__tests__/capability-e2e.test.ts +9 -11
  30. package/src/__tests__/cli-e2e.test.ts +80 -59
  31. package/src/__tests__/core-cap-bridge.spec.ts +3 -1
  32. package/src/__tests__/dev-bootstrap-shm-ring.spec.ts +12 -2
  33. package/src/__tests__/device-settings-contribution-dispatch.spec.ts +61 -30
  34. package/src/__tests__/embedded-deps-e2e.test.ts +35 -19
  35. package/src/__tests__/event-bus-proxy-router.spec.ts +3 -0
  36. package/src/__tests__/framework-allowlist.spec.ts +5 -4
  37. package/src/__tests__/https-e2e.test.ts +12 -6
  38. package/src/__tests__/lifecycle-e2e.test.ts +60 -11
  39. package/src/__tests__/live-events-subscription.spec.ts +17 -18
  40. package/src/__tests__/moleculer/uds-readiness.spec.ts +11 -4
  41. package/src/__tests__/moleculer/uds-topology.spec.ts +39 -11
  42. package/src/__tests__/moleculer/uds-unowned-call.spec.ts +71 -17
  43. package/src/__tests__/moleculer-register-node-idempotency.spec.ts +16 -7
  44. package/src/__tests__/native-cap-route.spec.ts +42 -19
  45. package/src/__tests__/oauth2-account-linking.spec.ts +63 -17
  46. package/src/__tests__/singleton-contention.test.ts +23 -11
  47. package/src/__tests__/streaming-diagnostic.test.ts +156 -53
  48. package/src/__tests__/streaming-scale.test.ts +69 -35
  49. package/src/__tests__/uds-addon-call-wiring.spec.ts +6 -1
  50. package/src/agent-status-page.ts +4 -3
  51. package/src/api/__tests__/addons-custom.spec.ts +22 -8
  52. package/src/api/__tests__/capabilities.router.test.ts +18 -9
  53. package/src/api/addon-upload.ts +46 -15
  54. package/src/api/addons-custom.router.ts +7 -6
  55. package/src/api/auth-whoami.ts +3 -1
  56. package/src/api/bridge-addons.router.ts +3 -1
  57. package/src/api/capabilities.router.ts +117 -78
  58. package/src/api/core/__tests__/auth-router-totp.spec.ts +57 -16
  59. package/src/api/core/addon-settings.router.ts +4 -1
  60. package/src/api/core/agents.router.ts +52 -53
  61. package/src/api/core/auth.router.ts +55 -36
  62. package/src/api/core/bulk-update-coordinator.ts +25 -22
  63. package/src/api/core/cap-providers.ts +346 -202
  64. package/src/api/core/capabilities.router.ts +30 -23
  65. package/src/api/core/hwaccel.router.ts +37 -10
  66. package/src/api/core/live-events.router.ts +16 -9
  67. package/src/api/core/logs.router.ts +54 -25
  68. package/src/api/core/notifications.router.ts +2 -1
  69. package/src/api/core/repl.router.ts +1 -3
  70. package/src/api/core/settings-backend.router.ts +68 -70
  71. package/src/api/core/system-events.router.ts +41 -32
  72. package/src/api/health/health.routes.ts +7 -13
  73. package/src/api/oauth2/__tests__/oauth2-routes.spec.ts +12 -2
  74. package/src/api/oauth2/consent-page.ts +4 -3
  75. package/src/api/oauth2/oauth2-routes.ts +41 -12
  76. package/src/api/trpc/__tests__/scope-access-device.spec.ts +68 -23
  77. package/src/api/trpc/__tests__/scope-access.spec.ts +8 -13
  78. package/src/api/trpc/__tests__/webrtc-session-ua-enrich.spec.ts +10 -2
  79. package/src/api/trpc/cap-mount-helpers.ts +64 -55
  80. package/src/api/trpc/cap-route-error-formatter.ts +17 -9
  81. package/src/api/trpc/core-cap-bridge.ts +3 -1
  82. package/src/api/trpc/generated-cap-mounts.ts +593 -351
  83. package/src/api/trpc/generated-cap-routers.ts +3680 -579
  84. package/src/api/trpc/scope-access.ts +7 -7
  85. package/src/api/trpc/trpc.context.ts +7 -4
  86. package/src/api/trpc/trpc.middleware.ts +4 -2
  87. package/src/api/trpc/trpc.router.ts +79 -46
  88. package/src/auth/session-cookie.ts +10 -0
  89. package/src/boot/__tests__/integration-id-backfill.spec.ts +21 -6
  90. package/src/boot/boot-config.ts +103 -122
  91. package/src/boot/post-boot.service.ts +5 -3
  92. package/src/core/addon/__tests__/addon-registry-capability.test.ts +12 -3
  93. package/src/core/addon/addon-call-gateway.ts +20 -6
  94. package/src/core/addon/addon-package.service.ts +183 -89
  95. package/src/core/addon/addon-registry.service.ts +1163 -1305
  96. package/src/core/addon/addon-search.service.ts +2 -1
  97. package/src/core/addon/addon-settings-provider.ts +27 -7
  98. package/src/core/addon-bridge/addon-bridge.service.ts +11 -6
  99. package/src/core/addon-pages/addon-pages.service.ts +3 -1
  100. package/src/core/addon-widgets/addon-widgets.service.ts +5 -2
  101. package/src/core/agent/agent-registry.service.ts +60 -38
  102. package/src/core/auth/auth.service.spec.ts +6 -8
  103. package/src/core/config/config.service.spec.ts +1 -1
  104. package/src/core/events/event-bus.service.spec.ts +44 -21
  105. package/src/core/events/event-bus.service.ts +5 -1
  106. package/src/core/feature/feature.service.spec.ts +4 -1
  107. package/src/core/lifecycle/lifecycle-state-machine.spec.ts +8 -10
  108. package/src/core/logging/logging.service.spec.ts +61 -21
  109. package/src/core/logging/logging.service.ts +12 -3
  110. package/src/core/moleculer/cap-call-fn.spec.ts +17 -10
  111. package/src/core/moleculer/cap-call-fn.ts +5 -1
  112. package/src/core/moleculer/cap-route-authority.ts +18 -6
  113. package/src/core/moleculer/moleculer.service.ts +120 -32
  114. package/src/core/network/network-quality.service.spec.ts +6 -1
  115. package/src/core/notification/notification-wrapper.service.ts +1 -3
  116. package/src/core/notification/toast-wrapper.service.ts +1 -5
  117. package/src/core/repl/repl-engine.service.spec.ts +66 -39
  118. package/src/core/repl/repl-engine.service.ts +11 -12
  119. package/src/core/storage/storage-location-manager.spec.ts +12 -3
  120. package/src/core/streaming/stream-probe.service.ts +22 -13
  121. package/src/core/topology/topology-emitter.service.ts +5 -1
  122. package/src/launcher.ts +14 -9
  123. package/src/main.ts +602 -531
  124. package/src/manual-boot.ts +133 -154
  125. package/tsconfig.json +20 -8
@@ -4,26 +4,26 @@
4
4
  * Extracted from main.ts — pure extraction, no behavior change.
5
5
  */
6
6
 
7
- import * as fs from "node:fs";
8
- import * as path from "node:path";
9
- import * as yaml from "js-yaml";
10
- import { randomBytes } from "node:crypto";
11
- import { asJsonObject } from "@camstack/types";
12
- import { bootstrapSchema } from "../core/config/config.schema";
13
- import type { BootstrapConfig } from "../core/config/config.schema";
14
- import { StorageLocationManager } from "../core/storage/storage-location-manager";
7
+ import * as fs from 'node:fs'
8
+ import * as path from 'node:path'
9
+ import * as yaml from 'js-yaml'
10
+ import { randomBytes } from 'node:crypto'
11
+ import { asJsonObject } from '@camstack/types'
12
+ import { bootstrapSchema } from '../core/config/config.schema'
13
+ import type { BootstrapConfig } from '../core/config/config.schema'
14
+ import { StorageLocationManager } from '../core/storage/storage-location-manager'
15
15
 
16
16
  // ---------------------------------------------------------------------------
17
17
  // Types
18
18
  // ---------------------------------------------------------------------------
19
19
 
20
- export type { BootstrapConfig };
20
+ export type { BootstrapConfig }
21
21
 
22
22
  export interface InfraContext {
23
- readonly bootstrapConfig: BootstrapConfig;
24
- readonly dataPath: string;
25
- readonly locationManager: StorageLocationManager;
26
- readonly tlsOptions: { key: Buffer; cert: Buffer } | undefined;
23
+ readonly bootstrapConfig: BootstrapConfig
24
+ readonly dataPath: string
25
+ readonly locationManager: StorageLocationManager
26
+ readonly tlsOptions: { key: Buffer; cert: Buffer } | undefined
27
27
  }
28
28
 
29
29
  // ---------------------------------------------------------------------------
@@ -31,28 +31,28 @@ export interface InfraContext {
31
31
  // ---------------------------------------------------------------------------
32
32
 
33
33
  const CONFIG_DEFAULTS: Record<string, unknown> = {
34
- server: { port: 4443, host: "0.0.0.0", dataPath: "camstack-data" },
34
+ server: { port: 4443, host: '0.0.0.0', dataPath: 'camstack-data' },
35
35
  auth: {
36
36
  jwtSecret: null,
37
- adminUsername: "admin",
38
- adminPassword: "changeme",
37
+ adminUsername: 'admin',
38
+ adminPassword: 'changeme',
39
39
  },
40
- };
40
+ }
41
41
 
42
42
  const ENV_VAR_MAP: Record<string, string> = {
43
- CAMSTACK_PORT: "server.port",
44
- CAMSTACK_HOST: "server.host",
45
- CAMSTACK_DATA: "server.dataPath",
46
- CAMSTACK_JWT_SECRET: "auth.jwtSecret",
47
- CAMSTACK_ADMIN_USER: "auth.adminUsername",
48
- CAMSTACK_ADMIN_PASS: "auth.adminPassword",
49
- CAMSTACK_HUB_URL: "hub.url",
50
- CAMSTACK_HUB_TOKEN: "hub.token",
51
- CAMSTACK_AGENT_NAME: "agent.name",
52
- CAMSTACK_TLS_ENABLED: "tls.enabled",
53
- CAMSTACK_TLS_CERT: "tls.certPath",
54
- CAMSTACK_TLS_KEY: "tls.keyPath",
55
- };
43
+ CAMSTACK_PORT: 'server.port',
44
+ CAMSTACK_HOST: 'server.host',
45
+ CAMSTACK_DATA: 'server.dataPath',
46
+ CAMSTACK_JWT_SECRET: 'auth.jwtSecret',
47
+ CAMSTACK_ADMIN_USER: 'auth.adminUsername',
48
+ CAMSTACK_ADMIN_PASS: 'auth.adminPassword',
49
+ CAMSTACK_HUB_URL: 'hub.url',
50
+ CAMSTACK_HUB_TOKEN: 'hub.token',
51
+ CAMSTACK_AGENT_NAME: 'agent.name',
52
+ CAMSTACK_TLS_ENABLED: 'tls.enabled',
53
+ CAMSTACK_TLS_CERT: 'tls.certPath',
54
+ CAMSTACK_TLS_KEY: 'tls.keyPath',
55
+ }
56
56
 
57
57
  // ---------------------------------------------------------------------------
58
58
  // Helpers
@@ -63,11 +63,11 @@ function setNested(
63
63
  p: string,
64
64
  value: unknown,
65
65
  ): Record<string, unknown> {
66
- const [head, ...rest] = p.split(".");
67
- if (!head) return obj;
68
- if (rest.length === 0) return { ...obj, [head]: value };
69
- const child = asJsonObject(obj[head]) ?? {};
70
- return { ...obj, [head]: setNested(child, rest.join("."), value) };
66
+ const [head, ...rest] = p.split('.')
67
+ if (!head) return obj
68
+ if (rest.length === 0) return { ...obj, [head]: value }
69
+ const child = asJsonObject(obj[head]) ?? {}
70
+ return { ...obj, [head]: setNested(child, rest.join('.'), value) }
71
71
  }
72
72
 
73
73
  // ---------------------------------------------------------------------------
@@ -82,72 +82,68 @@ export function loadBootstrapConfig(configPath: string): BootstrapConfig {
82
82
  // Only bootstrap sections live in config.yaml.
83
83
  // All runtime settings are stored in the SQL system_settings table.
84
84
 
85
- let raw: Record<string, unknown>;
85
+ let raw: Record<string, unknown>
86
86
 
87
87
  if (fs.existsSync(configPath)) {
88
- const content = fs.readFileSync(configPath, "utf-8");
89
- raw = asJsonObject(yaml.load(content)) ?? {};
88
+ const content = fs.readFileSync(configPath, 'utf-8')
89
+ raw = asJsonObject(yaml.load(content)) ?? {}
90
90
  // Merge in any missing bootstrap sections (server, auth only)
91
- let updated = false;
91
+ let updated = false
92
92
  for (const [key, defaults] of Object.entries(CONFIG_DEFAULTS)) {
93
93
  if (!(key in raw)) {
94
- raw[key] = defaults;
95
- updated = true;
94
+ raw[key] = defaults
95
+ updated = true
96
96
  }
97
97
  }
98
98
  if (updated) {
99
99
  try {
100
- const tmpPath = `${configPath}.tmp`;
100
+ const tmpPath = `${configPath}.tmp`
101
101
  fs.writeFileSync(
102
102
  tmpPath,
103
103
  yaml.dump(raw, { lineWidth: 120, indent: 2, quotingType: '"' }),
104
- "utf-8",
105
- );
106
- fs.renameSync(tmpPath, configPath);
107
- console.log(
108
- `[Phase1] Updated config.yaml with missing bootstrap defaults`,
109
- );
104
+ 'utf-8',
105
+ )
106
+ fs.renameSync(tmpPath, configPath)
107
+ console.log(`[Phase1] Updated config.yaml with missing bootstrap defaults`)
110
108
  } catch (err) {
111
- console.warn(`[Phase1] Could not update config.yaml:`, err);
109
+ console.warn(`[Phase1] Could not update config.yaml:`, err)
112
110
  }
113
111
  }
114
- console.log(`[Phase1] Loaded bootstrap config from: ${configPath}`);
112
+ console.log(`[Phase1] Loaded bootstrap config from: ${configPath}`)
115
113
  } else {
116
- console.log(
117
- `[Phase1] Config file not found at: ${configPath} — writing defaults`,
118
- );
119
- const defaults = { ...CONFIG_DEFAULTS };
114
+ console.log(`[Phase1] Config file not found at: ${configPath} — writing defaults`)
115
+ const defaults = { ...CONFIG_DEFAULTS }
120
116
  try {
121
- fs.mkdirSync(path.dirname(configPath), { recursive: true });
122
- const tmpPath = `${configPath}.tmp`;
117
+ fs.mkdirSync(path.dirname(configPath), { recursive: true })
118
+ const tmpPath = `${configPath}.tmp`
123
119
  fs.writeFileSync(
124
120
  tmpPath,
125
121
  yaml.dump(defaults, { lineWidth: 120, indent: 2, quotingType: '"' }),
126
- "utf-8",
127
- );
128
- fs.renameSync(tmpPath, configPath);
129
- console.log(`[Phase1] Default config.yaml written to: ${configPath}`);
122
+ 'utf-8',
123
+ )
124
+ fs.renameSync(tmpPath, configPath)
125
+ console.log(`[Phase1] Default config.yaml written to: ${configPath}`)
130
126
  } catch (err) {
131
- console.warn(`[Phase1] Could not write default config.yaml:`, err);
127
+ console.warn(`[Phase1] Could not write default config.yaml:`, err)
132
128
  }
133
- raw = defaults;
129
+ raw = defaults
134
130
  }
135
131
 
136
132
  // Apply env var overrides for bootstrap keys
137
133
  for (const [envKey, configPath_] of Object.entries(ENV_VAR_MAP)) {
138
- const envValue = process.env[envKey];
139
- if (envValue === undefined || envValue === "") continue;
134
+ const envValue = process.env[envKey]
135
+ if (envValue === undefined || envValue === '') continue
140
136
  const coerced: unknown =
141
- configPath_ === "server.port"
137
+ configPath_ === 'server.port'
142
138
  ? Number(envValue)
143
- : configPath_ === "tls.enabled"
144
- ? envValue === "true"
145
- : envValue;
146
- raw = setNested(raw, configPath_, coerced);
147
- console.log(`[Phase1] Env override: ${envKey} → ${configPath_}`);
139
+ : configPath_ === 'tls.enabled'
140
+ ? envValue === 'true'
141
+ : envValue
142
+ raw = setNested(raw, configPath_, coerced)
143
+ console.log(`[Phase1] Env override: ${envKey} → ${configPath_}`)
148
144
  }
149
145
 
150
- return bootstrapSchema.parse(raw);
146
+ return bootstrapSchema.parse(raw)
151
147
  }
152
148
 
153
149
  // ---------------------------------------------------------------------------
@@ -163,42 +159,37 @@ export function autoGenerateJwtSecret(
163
159
  bootstrapConfig: BootstrapConfig,
164
160
  ): BootstrapConfig {
165
161
  if (bootstrapConfig.auth.jwtSecret !== null) {
166
- return bootstrapConfig;
162
+ return bootstrapConfig
167
163
  }
168
164
 
169
- const secret = randomBytes(32).toString("hex");
170
- console.log(
171
- "[Phase1] jwtSecret is null — auto-generating and writing to config.yaml",
172
- );
165
+ const secret = randomBytes(32).toString('hex')
166
+ console.log('[Phase1] jwtSecret is null — auto-generating and writing to config.yaml')
173
167
 
174
- let raw: Record<string, unknown> = {};
168
+ let raw: Record<string, unknown> = {}
175
169
  if (fs.existsSync(configPath)) {
176
- raw = asJsonObject(yaml.load(fs.readFileSync(configPath, "utf-8"))) ?? {};
170
+ raw = asJsonObject(yaml.load(fs.readFileSync(configPath, 'utf-8'))) ?? {}
177
171
  }
178
172
 
179
- const authSection = asJsonObject(raw.auth) ?? {};
180
- raw.auth = { ...authSection, jwtSecret: secret };
173
+ const authSection = asJsonObject(raw.auth) ?? {}
174
+ raw.auth = { ...authSection, jwtSecret: secret }
181
175
 
182
- const tmpPath = `${configPath}.tmp`;
176
+ const tmpPath = `${configPath}.tmp`
183
177
  try {
184
- fs.mkdirSync(path.dirname(configPath), { recursive: true });
178
+ fs.mkdirSync(path.dirname(configPath), { recursive: true })
185
179
  fs.writeFileSync(
186
180
  tmpPath,
187
181
  yaml.dump(raw, { lineWidth: 120, indent: 2, quotingType: '"' }),
188
- "utf-8",
189
- );
190
- fs.renameSync(tmpPath, configPath);
182
+ 'utf-8',
183
+ )
184
+ fs.renameSync(tmpPath, configPath)
191
185
  } catch (err) {
192
- console.warn(
193
- "[Phase1] Could not write auto-generated jwtSecret to config.yaml:",
194
- err,
195
- );
186
+ console.warn('[Phase1] Could not write auto-generated jwtSecret to config.yaml:', err)
196
187
  }
197
188
 
198
189
  return {
199
190
  ...bootstrapConfig,
200
191
  auth: { ...bootstrapConfig.auth, jwtSecret: secret },
201
- };
192
+ }
202
193
  }
203
194
 
204
195
  // ---------------------------------------------------------------------------
@@ -214,58 +205,48 @@ export async function setupInfra(
214
205
  bootstrapConfig: BootstrapConfig,
215
206
  ): Promise<InfraContext> {
216
207
  // Auto-generate jwtSecret if not set
217
- const config = autoGenerateJwtSecret(configPath, bootstrapConfig);
208
+ const config = autoGenerateJwtSecret(configPath, bootstrapConfig)
218
209
 
219
- const dataPath = path.resolve(config.server.dataPath);
220
- const port = config.server.port;
221
- const host = config.server.host;
210
+ const dataPath = path.resolve(config.server.dataPath)
211
+ const port = config.server.port
212
+ const host = config.server.host
222
213
 
223
- console.log(
224
- `[Phase1] Bootstrap: port=${port}, host=${host}, dataPath=${dataPath}`,
225
- );
214
+ console.log(`[Phase1] Bootstrap: port=${port}, host=${host}, dataPath=${dataPath}`)
226
215
 
227
216
  // --- Phase 2: Init StorageLocationManager → ensure all dirs exist ---
228
- console.log("[Phase2] Initializing storage locations…");
229
- const locationManager = new StorageLocationManager(dataPath);
230
- await locationManager.initializeDefaults();
217
+ console.log('[Phase2] Initializing storage locations…')
218
+ const locationManager = new StorageLocationManager(dataPath)
219
+ await locationManager.initializeDefaults()
231
220
 
232
- const locationStatus = locationManager.getStatus();
221
+ const locationStatus = locationManager.getStatus()
233
222
  for (const { name, available, path: locPath } of locationStatus) {
234
- console.log(
235
- `[Phase2] Location "${name}": ${available ? "OK" : "UNAVAILABLE"} → ${locPath}`,
236
- );
223
+ console.log(`[Phase2] Location "${name}": ${available ? 'OK' : 'UNAVAILABLE'} → ${locPath}`)
237
224
  }
238
225
 
239
226
  // --- Phase 2c: TLS certificate setup ---
240
- let tlsOptions: { key: Buffer; cert: Buffer } | undefined;
227
+ let tlsOptions: { key: Buffer; cert: Buffer } | undefined
241
228
 
242
229
  if (config.tls.enabled) {
243
230
  // Use require() instead of import() — the ESM build of @camstack/core has
244
231
  // broken chunks with require("fs") when leaked .js files exist in core/src/.
245
232
  // CJS build works correctly and tsx supports require().
246
- const core = require("@camstack/core") as typeof import("@camstack/core");
247
- const { ensureTlsCert, loadTlsCert } = core;
233
+ const core = require('@camstack/core') as typeof import('@camstack/core')
234
+ const { ensureTlsCert, loadTlsCert } = core
248
235
  if (config.tls.certPath && config.tls.keyPath) {
249
236
  // User-provided cert
250
- console.log(
251
- `[Phase2c] Loading custom TLS cert from ${config.tls.certPath}`,
252
- );
253
- const pair = loadTlsCert(config.tls.certPath, config.tls.keyPath);
254
- tlsOptions = { key: pair.key, cert: pair.cert };
237
+ console.log(`[Phase2c] Loading custom TLS cert from ${config.tls.certPath}`)
238
+ const pair = loadTlsCert(config.tls.certPath, config.tls.keyPath)
239
+ tlsOptions = { key: pair.key, cert: pair.cert }
255
240
  } else {
256
241
  // Auto-generate self-signed
257
- const tlsResult = await ensureTlsCert(dataPath);
242
+ const tlsResult = await ensureTlsCert(dataPath)
258
243
  if (tlsResult.generated) {
259
- console.log(
260
- `[Phase2c] Generated self-signed TLS cert at ${tlsResult.certPath}`,
261
- );
244
+ console.log(`[Phase2c] Generated self-signed TLS cert at ${tlsResult.certPath}`)
262
245
  } else {
263
- console.log(
264
- `[Phase2c] Using existing TLS cert at ${tlsResult.certPath}`,
265
- );
246
+ console.log(`[Phase2c] Using existing TLS cert at ${tlsResult.certPath}`)
266
247
  }
267
- const pair = loadTlsCert(tlsResult.certPath, tlsResult.keyPath);
268
- tlsOptions = { key: pair.key, cert: pair.cert };
248
+ const pair = loadTlsCert(tlsResult.certPath, tlsResult.keyPath)
249
+ tlsOptions = { key: pair.key, cert: pair.cert }
269
250
  }
270
251
  }
271
252
 
@@ -274,5 +255,5 @@ export async function setupInfra(
274
255
  dataPath,
275
256
  locationManager,
276
257
  tlsOptions,
277
- };
258
+ }
278
259
  }
@@ -1,5 +1,6 @@
1
1
  import { randomUUID } from 'node:crypto'
2
2
  import { readPendingRestart } from '@camstack/kernel'
3
+ import { EventCategory } from '@camstack/types'
3
4
  import type { IScopedLogger, PendingRestartMarkerPayload } from '@camstack/types'
4
5
  import { AddonRegistryService } from '../core/addon/addon-registry.service'
5
6
  import { EventBusService } from '../core/events/event-bus.service'
@@ -21,7 +22,8 @@ export class PostBootService {
21
22
  * after they reconnect — the live event itself is emitted before the
22
23
  * WS resubscribe has a chance to land.
23
24
  */
24
- private static lastRestart: { payload: PendingRestartMarkerPayload; expiresAt: number } | null = null
25
+ private static lastRestart: { payload: PendingRestartMarkerPayload; expiresAt: number } | null =
26
+ null
25
27
 
26
28
  /** How long the last-restart marker stays queryable after boot (5 min). */
27
29
  static readonly LAST_RESTART_RETENTION_MS = 5 * 60_000
@@ -62,7 +64,7 @@ export class PostBootService {
62
64
  id: randomUUID(),
63
65
  timestamp: new Date(),
64
66
  source: { type: 'core', id: 'system' },
65
- category: 'system.boot',
67
+ category: EventCategory.SystemBoot,
66
68
  data: { port, host, trpcRegistered, dataPath },
67
69
  })
68
70
 
@@ -96,7 +98,7 @@ export class PostBootService {
96
98
  id: randomUUID(),
97
99
  timestamp: new Date(),
98
100
  source: { type: 'core', id: 'system' },
99
- category: 'system.restart-completed',
101
+ category: EventCategory.SystemRestartCompleted,
100
102
  data: payload,
101
103
  })
102
104
  }
@@ -1,4 +1,3 @@
1
-
2
1
  // server/backend/src/core/addon/__tests__/addon-registry-capability.test.ts
3
2
  import { describe, it, expect, vi, beforeEach } from 'vitest'
4
3
  import { CapabilityRegistry } from '@camstack/kernel'
@@ -32,7 +31,12 @@ describe('AddonRegistryService -- CapabilityRegistry integration', () => {
32
31
  })
33
32
 
34
33
  it('streaming-engine singleton wired after provider registers', () => {
35
- registry.declareCapability({ name: 'streaming-engine', scope: 'system', mode: 'singleton', methods: {} })
34
+ registry.declareCapability({
35
+ name: 'streaming-engine',
36
+ scope: 'system',
37
+ mode: 'singleton',
38
+ methods: {},
39
+ })
36
40
 
37
41
  const mockEngine = { initialize: vi.fn() }
38
42
  registry.registerProvider('streaming-engine', 'go2rtc', mockEngine)
@@ -41,7 +45,12 @@ describe('AddonRegistryService -- CapabilityRegistry integration', () => {
41
45
  })
42
46
 
43
47
  it('log-destination collection receives all providers', () => {
44
- registry.declareCapability({ name: 'log-destination', scope: 'system', mode: 'collection', methods: {} })
48
+ registry.declareCapability({
49
+ name: 'log-destination',
50
+ scope: 'system',
51
+ mode: 'collection',
52
+ methods: {},
53
+ })
45
54
 
46
55
  const dest1 = { id: 'winston' }
47
56
  const dest2 = { id: 'loki' }
@@ -92,7 +92,11 @@ export class AddonCallGateway {
92
92
  * must invoke the in-process instance directly (the invocation is
93
93
  * surface-specific; only the ROUTING is centralised here).
94
94
  */
95
- async callForked(addonId: string, input: AddonCallSurface, explicitNodeId?: string): Promise<unknown> {
95
+ async callForked(
96
+ addonId: string,
97
+ input: AddonCallSurface,
98
+ explicitNodeId?: string,
99
+ ): Promise<unknown> {
96
100
  const dest = this.classify(addonId, explicitNodeId)
97
101
  const fullInput: AddonCallInput = { ...input, addonId }
98
102
  switch (dest.kind) {
@@ -115,7 +119,11 @@ export class AddonCallGateway {
115
119
  }
116
120
 
117
121
  /** Map an addon-level call to the remote agent's Moleculer action. */
118
- private async callRemoteAgent(addonId: string, baseNodeId: string, input: AddonCallInput): Promise<unknown> {
122
+ private async callRemoteAgent(
123
+ addonId: string,
124
+ baseNodeId: string,
125
+ input: AddonCallInput,
126
+ ): Promise<unknown> {
119
127
  const workerNodeId = this.resolveWorkerNodeId(addonId, baseNodeId)
120
128
  const opts = workerNodeId
121
129
  ? { nodeID: workerNodeId, timeout: REMOTE_TIMEOUT_MS }
@@ -132,7 +140,9 @@ export class AddonCallGateway {
132
140
  }
133
141
  // routes/custom are hub-local-child surfaces (mounted / invoked on the
134
142
  // owning node); they are not proxied to a remote agent through this gateway.
135
- throw new Error(`AddonCallGateway: target "${input.target}" not supported for remote agent "${baseNodeId}"`)
143
+ throw new Error(
144
+ `AddonCallGateway: target "${input.target}" not supported for remote agent "${baseNodeId}"`,
145
+ )
136
146
  }
137
147
 
138
148
  /**
@@ -143,9 +153,13 @@ export class AddonCallGateway {
143
153
  */
144
154
  private resolveWorkerNodeId(addonId: string, baseNodeId: string): string | null {
145
155
  const registry = this.deps.broker.registry
146
- const services = (registry as unknown as {
147
- getServiceList: (opts: { onlyAvailable: boolean }) => readonly { name: string; nodeID: string }[]
148
- }).getServiceList({ onlyAvailable: true })
156
+ const services = (
157
+ registry as unknown as {
158
+ getServiceList: (opts: {
159
+ onlyAvailable: boolean
160
+ }) => readonly { name: string; nodeID: string }[]
161
+ }
162
+ ).getServiceList({ onlyAvailable: true })
149
163
  const exactNode = `${baseNodeId}/${addonId}`
150
164
  const preferred = services.find((s) => s.name === addonId && s.nodeID === exactNode)
151
165
  if (preferred) return preferred.nodeID