@camstack/server 1.0.1 → 1.0.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 (39) hide show
  1. package/dist/api/addon-upload.js +17 -3
  2. package/dist/api/bridge-addons.router.js +1 -1
  3. package/dist/api/capabilities.router.js +3 -3
  4. package/dist/api/core/cap-providers.js +3 -3
  5. package/dist/api/core/logs.router.js +1 -1
  6. package/dist/api/core/notifications.router.js +1 -1
  7. package/dist/api/core/repl.router.js +1 -1
  8. package/dist/api/health/health.routes.js +7 -2
  9. package/dist/api/trpc/cap-route-error-formatter.js +3 -3
  10. package/dist/api/trpc/core-cap-bridge.js +2 -2
  11. package/dist/api/trpc/generated-cap-routers.js +4 -4
  12. package/dist/boot/boot-config.js +2 -2
  13. package/dist/boot/post-boot.service.js +2 -2
  14. package/dist/core/addon/addon-call-gateway.js +1 -1
  15. package/dist/core/addon/addon-package.service.js +37 -43
  16. package/dist/core/addon/addon-registry.service.js +67 -52
  17. package/dist/core/addon-bridge/addon-bridge.service.js +6 -6
  18. package/dist/core/addon-pages/addon-pages.service.js +1 -1
  19. package/dist/core/auth/auth.service.js +2 -2
  20. package/dist/core/config/config.schema.js +4 -4
  21. package/dist/core/config/config.service.js +2 -2
  22. package/dist/core/events/event-bus.service.js +2 -2
  23. package/dist/core/feature/feature.service.js +2 -2
  24. package/dist/core/lifecycle/lifecycle-state-machine.js +3 -3
  25. package/dist/core/logging/log-ring-buffer.js +3 -3
  26. package/dist/core/logging/logging.service.js +3 -3
  27. package/dist/core/logging/scoped-logger.js +3 -3
  28. package/dist/core/moleculer/cap-route-authority.js +2 -2
  29. package/dist/core/moleculer/moleculer.service.js +29 -23
  30. package/dist/core/network/network-quality.service.js +2 -2
  31. package/dist/core/notification/notification-wrapper.service.js +2 -2
  32. package/dist/core/notification/toast-wrapper.service.js +2 -2
  33. package/dist/core/repl/repl-engine.service.js +2 -2
  34. package/dist/core/storage/fs-storage-backend.js +3 -3
  35. package/dist/core/storage/storage-location-manager.js +3 -3
  36. package/dist/core/storage/storage.service.js +2 -2
  37. package/dist/launcher.js +23 -12
  38. package/dist/main.js +14 -5
  39. package/package.json +2 -3
@@ -322,9 +322,23 @@ async function installToHub(reply, addonBridge, addonRegistry, addonPackageServi
322
322
  .listAddons()
323
323
  .filter((row) => row.manifest.packageName === result.name)
324
324
  .map((row) => row.manifest.id);
325
- const addonsDir = path.resolve(process.env['CAMSTACK_DATA'] ?? 'camstack-data', 'addons');
326
- fs.writeFileSync(path.join(addonsDir, result.name, '.install-source'), 'upload');
327
- // Framework / system packages (`camstack.system: true`, e.g. @camstack/core)
325
+ // Must honour CAMSTACK_ADDONS_DIR the runtime installs/loads addons
326
+ // from there (e.g. /data/node_modules), NOT from CAMSTACK_DATA/addons.
327
+ // Deriving the dir from CAMSTACK_DATA alone pointed the marker write at a
328
+ // non-existent path → unguarded ENOENT → HTTP 500, which aborted the
329
+ // handler before loadNewAddons()/restartAddon() ran, so uploads landed on
330
+ // disk but never hot-reloaded. Mirrors AddonPackageService.resolveAddonsDir().
331
+ const dataDir = path.resolve(process.env['CAMSTACK_DATA'] ?? 'camstack-data');
332
+ const addonsDir = process.env['CAMSTACK_ADDONS_DIR'] ?? path.resolve(dataDir, 'addons');
333
+ try {
334
+ fs.writeFileSync(path.join(addonsDir, result.name, '.install-source'), 'upload');
335
+ }
336
+ catch (err) {
337
+ logger.debug('Non-fatal: failed to write .install-source marker', {
338
+ meta: { error: err instanceof Error ? err.message : String(err) },
339
+ });
340
+ }
341
+ // Framework / system packages (`camstack.system: true`, e.g. @camstack/system)
328
342
  // ship hub builtins (sqlite-settings, filesystem-storage, …) that CANNOT be
329
343
  // hot-reloaded in place — an in-process `restartAddon` leaves them
330
344
  // uninitialized ("SqliteSettingsBackend not initialized"), breaking auth +
@@ -56,7 +56,7 @@ function createBridgeAddonsRouter(bridge, addonSearch, addonRegistry, toastServi
56
56
  // (stream-broker, detection-pipeline, motion-wasm, decoders,
57
57
  // audio) all live in @camstack/addon-pipeline.
58
58
  const REQUIRED = new Set([
59
- '@camstack/core',
59
+ '@camstack/system',
60
60
  '@camstack/addon-pipeline',
61
61
  '@camstack/addon-pipeline-orchestrator',
62
62
  '@camstack/addon-post-analysis',
@@ -4,7 +4,7 @@ exports.createCapabilitiesRouter = createCapabilitiesRouter;
4
4
  const zod_1 = require("zod");
5
5
  const server_1 = require("@trpc/server");
6
6
  const trpc_middleware_1 = require("./trpc/trpc.middleware");
7
- const kernel_1 = require("@camstack/kernel");
7
+ const system_1 = require("@camstack/system");
8
8
  const collection_preference_1 = require("./core/collection-preference");
9
9
  // ─── Zod Schemas ───────────────────────────────────────────────────────────
10
10
  const setPreferenceInput = zod_1.z.discriminatedUnion('mode', [
@@ -43,7 +43,7 @@ function createCapabilitiesRouter(addonRegistry, configService, loggingService)
43
43
  return caps.map((cap) => ({
44
44
  ...cap,
45
45
  providerDetails: getProviderDetails(cap.providers),
46
- isInfra: (0, kernel_1.isInfraCapability)(cap.name),
46
+ isInfra: (0, system_1.isInfraCapability)(cap.name),
47
47
  }));
48
48
  }),
49
49
  // ─── Get current preference for a capability ──────────────────────
@@ -98,7 +98,7 @@ function createCapabilitiesRouter(addonRegistry, configService, loggingService)
98
98
  message: `Provider "${addonId}" is not registered for "${capability}"`,
99
99
  });
100
100
  }
101
- const requiresRestart = (0, kernel_1.isInfraCapability)(capability);
101
+ const requiresRestart = (0, system_1.isInfraCapability)(capability);
102
102
  if (!requiresRestart) {
103
103
  // Hot-swap at runtime
104
104
  await registry.setActiveSingleton(capability, addonId);
@@ -72,7 +72,7 @@ const node_util_1 = require("node:util");
72
72
  const node_crypto_1 = require("node:crypto");
73
73
  const server_1 = require("@trpc/server");
74
74
  const types_1 = require("@camstack/types");
75
- const kernel_1 = require("@camstack/kernel");
75
+ const system_1 = require("@camstack/system");
76
76
  const integration_id_backfill_1 = require("../../boot/integration-id-backfill");
77
77
  const collection_preference_js_1 = require("./collection-preference.js");
78
78
  const bulk_update_coordinator_js_1 = require("./bulk-update-coordinator.js");
@@ -445,7 +445,7 @@ function buildNodesProvider(agentRegistry, moleculer, addonRegistry) {
445
445
  return result;
446
446
  },
447
447
  getCapUsageGraph: async (input) => {
448
- const reg = (0, kernel_1.getCapUsageRegistry)();
448
+ const reg = (0, system_1.getCapUsageRegistry)();
449
449
  return reg.getGraph({ windowSeconds: input.windowSeconds, nowMs: Date.now() });
450
450
  },
451
451
  setProcessLogLevel: async (input) => {
@@ -942,7 +942,7 @@ function buildAddonsProvider(ar, ps, ls, moleculer, configService, ctx, eb) {
942
942
  },
943
943
  logger: ls.createLogger('bulk-update'),
944
944
  });
945
- const frameworkAllowSet = new Set(addon_package_service_js_1.FRAMEWORK_PACKAGE_ALLOWLIST);
945
+ const frameworkAllowSet = new Set([addon_package_service_js_1.SYSTEM_PACKAGE]);
946
946
  return {
947
947
  list: async () => {
948
948
  const rollbackable = ps.getRollbackablePackages();
@@ -5,7 +5,7 @@ exports.createLogsRouter = createLogsRouter;
5
5
  * Logs router — fixed core API (not a capability).
6
6
  *
7
7
  * Queries and streams log entries via `LoggingService` (thin NestJS DI
8
- * wrapper over `LogManager` from `@camstack/core`). Admin can read
8
+ * wrapper over `LogManager` from `@camstack/system`). Admin can read
9
9
  * everything; viewers/protected users only see entries matching their
10
10
  * allowed providers scope (filtered in `query`).
11
11
  */
@@ -5,7 +5,7 @@ exports.createNotificationsRouter = createNotificationsRouter;
5
5
  * Notifications router — fixed core API (not a capability).
6
6
  *
7
7
  * Routes events by category to registered outputs. The NotificationService
8
- * is a singleton from @camstack/core; individual outputs are still provided
8
+ * is a singleton from @camstack/system; individual outputs are still provided
9
9
  * by addons via the `notification-output` capability collection.
10
10
  */
11
11
  const zod_1 = require("zod");
@@ -5,7 +5,7 @@ exports.createReplRouter = createReplRouter;
5
5
  * REPL router — fixed core API (not a capability).
6
6
  *
7
7
  * The REPL engine is a single NestJS-backed service in the backend
8
- * (`ReplEngineService` extends `ReplEngine` from `@camstack/core`).
8
+ * (`ReplEngineService` extends `ReplEngine` from `@camstack/system`).
9
9
  * Only super_admin can execute code.
10
10
  */
11
11
  const zod_1 = require("zod");
@@ -14,7 +14,7 @@ async function buildHubHealth(deps, proc = process) {
14
14
  const totalMem = memUsage.heapTotal + memUsage.external + memUsage.arrayBuffers;
15
15
  const memoryPercent = totalMem > 0 ? Math.round((memUsage.heapUsed / totalMem) * 100) : 0;
16
16
  return {
17
- ok: true,
17
+ ok: deps.isReady(),
18
18
  nodeId: 'hub',
19
19
  version: deps.hubVersion,
20
20
  uptimeSeconds: Math.round(proc.uptime()),
@@ -39,7 +39,12 @@ async function fetchAgentHealth(deps, nodeId) {
39
39
  }
40
40
  }
41
41
  function registerHealthRoutes(fastify, deps) {
42
- fastify.get('/health', async () => buildHubHealth(deps));
42
+ fastify.get('/health', async (_req, reply) => {
43
+ const health = await buildHubHealth(deps);
44
+ if (!health.ok)
45
+ reply.status(503);
46
+ return health;
47
+ });
43
48
  fastify.get('/health/agents', async () => {
44
49
  const nodes = await deps.agentRegistry.listNodes();
45
50
  return {
@@ -16,7 +16,7 @@ exports.formatTrpcError = formatTrpcError;
16
16
  *
17
17
  * The formatter is EXPORTED for unit testing (no side-effects, pure function).
18
18
  */
19
- const kernel_1 = require("@camstack/kernel");
19
+ const system_1 = require("@camstack/system");
20
20
  // ---------------------------------------------------------------------------
21
21
  // Type guards
22
22
  // ---------------------------------------------------------------------------
@@ -56,7 +56,7 @@ function extractCapRouteError(err) {
56
56
  for (let depth = 0; depth < 8; depth++) {
57
57
  if (current === null || current === undefined)
58
58
  return null;
59
- if (current instanceof kernel_1.CapRouteError) {
59
+ if (current instanceof system_1.CapRouteError) {
60
60
  return current;
61
61
  }
62
62
  // Duck-type fallback: err.name + err.reason field present
@@ -83,7 +83,7 @@ function extractCapRouteError(err) {
83
83
  const rawCapName = Reflect.get(current, 'capName');
84
84
  const capName = typeof rawCapName === 'string' ? rawCapName : '(unknown)';
85
85
  // Build a minimal object with the same shape — enough for the formatter.
86
- const synthetic = Object.assign(new kernel_1.CapRouteError(capName, undefined, {
86
+ const synthetic = Object.assign(new system_1.CapRouteError(capName, undefined, {
87
87
  reason,
88
88
  rejected,
89
89
  ...(typeof nodeId === 'string' ? { nodeId } : {}),
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildCoreCapService = buildCoreCapService;
4
- const kernel_1 = require("@camstack/kernel");
4
+ const system_1 = require("@camstack/system");
5
5
  const trpc_middleware_js_1 = require("./trpc.middleware.js");
6
6
  const trpc_context_js_1 = require("./trpc.context.js");
7
7
  /**
@@ -111,5 +111,5 @@ function buildCoreCapService(appRouter) {
111
111
  const method = path.slice(dot + 1);
112
112
  actions.push({ actionName: `${toKebab(namespace)}.${method}`, invoke });
113
113
  }
114
- return (0, kernel_1.createCoreCapService)({ actions });
114
+ return (0, system_1.createCoreCapService)({ actions });
115
115
  }
@@ -140,7 +140,7 @@ exports.createCapRouter_zoneAnalytics = createCapRouter_zoneAnalytics;
140
140
  exports.createCapRouter_zoneRules = createCapRouter_zoneRules;
141
141
  exports.createCapRouter_zones = createCapRouter_zones;
142
142
  const server_1 = require("@trpc/server");
143
- const kernel_1 = require("@camstack/kernel");
143
+ const system_1 = require("@camstack/system");
144
144
  const zod_1 = require("zod");
145
145
  const trpc_middleware_js_1 = require("./trpc.middleware.js");
146
146
  const types_1 = require("@camstack/types");
@@ -259,7 +259,7 @@ function requireCapProvider(name, getProvider) {
259
259
  throw new server_1.TRPCError({
260
260
  code: 'PRECONDITION_FAILED',
261
261
  message: `Capability "${name}" provider not available`,
262
- cause: new kernel_1.CapRouteError(name, undefined, {
262
+ cause: new system_1.CapRouteError(name, undefined, {
263
263
  reason: 'no-provider',
264
264
  rejected: [{ kind: 'hub-in-process', why: 'no provider bound for this cap' }],
265
265
  }),
@@ -285,7 +285,7 @@ function resolveProvider(capName, nodeId, getLocal, createProxy) {
285
285
  throw new server_1.TRPCError({
286
286
  code: 'PRECONDITION_FAILED',
287
287
  message: `Capability "${capName}" provider not available`,
288
- cause: new kernel_1.CapRouteError(capName, undefined, {
288
+ cause: new system_1.CapRouteError(capName, undefined, {
289
289
  reason: 'no-provider',
290
290
  rejected: [{ kind: 'hub-in-process', why: 'no provider bound for this cap/device' }],
291
291
  }),
@@ -302,7 +302,7 @@ function resolveProvider(capName, nodeId, getLocal, createProxy) {
302
302
  throw new server_1.TRPCError({
303
303
  code: 'PRECONDITION_FAILED',
304
304
  message: `Capability "${capName}" not available on node "${nodeId}"`,
305
- cause: new kernel_1.CapRouteError(capName, undefined, {
305
+ cause: new system_1.CapRouteError(capName, undefined, {
306
306
  reason: 'no-provider',
307
307
  nodeId,
308
308
  rejected: [{ kind: 'remote-moleculer', why: `cap not available on node "${nodeId}"` }],
@@ -208,10 +208,10 @@ async function setupInfra(configPath, bootstrapConfig) {
208
208
  // --- Phase 2c: TLS certificate setup ---
209
209
  let tlsOptions;
210
210
  if (config.tls.enabled) {
211
- // Use require() instead of import() — the ESM build of @camstack/core has
211
+ // Use require() instead of import() — the ESM build of @camstack/system has
212
212
  // broken chunks with require("fs") when leaked .js files exist in core/src/.
213
213
  // CJS build works correctly and tsx supports require().
214
- const core = require('@camstack/core');
214
+ const core = require('@camstack/system');
215
215
  const { ensureTlsCert, loadTlsCert } = core;
216
216
  if (config.tls.certPath && config.tls.keyPath) {
217
217
  // User-provided cert
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PostBootService = void 0;
4
4
  const node_crypto_1 = require("node:crypto");
5
- const kernel_1 = require("@camstack/kernel");
5
+ const system_1 = require("@camstack/system");
6
6
  const types_1 = require("@camstack/types");
7
7
  class PostBootService {
8
8
  eventBus;
@@ -57,7 +57,7 @@ class PostBootService {
57
57
  this.emitRestartCompletedIfPending(dataPath);
58
58
  }
59
59
  emitRestartCompletedIfPending(dataDir) {
60
- const marker = (0, kernel_1.readPendingRestart)(dataDir);
60
+ const marker = (0, system_1.readPendingRestart)(dataDir);
61
61
  if (marker === null)
62
62
  return;
63
63
  const payload = {
@@ -11,7 +11,7 @@ class AddonCallGateway {
11
11
  * Classify where an addon runs. `nodeId: 'hub'` from a caller means "the hub
12
12
  * cluster", NOT "force in-process" — a forked hub-local addon is still a
13
13
  * `hub-local-child` (UDS), never the in-process path (which is only the
14
- * `@camstack/core` builtins that have no forked runner).
14
+ * `@camstack/system` builtins that have no forked runner).
15
15
  */
16
16
  classify(addonId, explicitNodeId) {
17
17
  const resolved = this.deps.resolveNode(addonId);
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.AddonPackageService = exports.FRAMEWORK_PACKAGE_ALLOWLIST = void 0;
36
+ exports.AddonPackageService = exports.SYSTEM_PACKAGE = void 0;
37
37
  exports.isFrameworkPackage = isFrameworkPackage;
38
38
  const fs = __importStar(require("node:fs"));
39
39
  const path = __importStar(require("node:path"));
@@ -42,30 +42,17 @@ const node_child_process_1 = require("node:child_process");
42
42
  const node_util_1 = require("node:util");
43
43
  const node_crypto_1 = require("node:crypto");
44
44
  const types_1 = require("@camstack/types");
45
- const kernel_1 = require("@camstack/kernel");
45
+ const system_1 = require("@camstack/system");
46
46
  const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
47
47
  /**
48
- * Framework packages (manifest `camstack.system: true`) that the
49
- * `updateFrameworkPackage` cap method is allowed to update. Any other
50
- * `packageName` is rejected.
51
- *
52
- * Mirror of `camstack.system: true` in
53
- * packages/{types,kernel,core,sdk,ui-library,shm-ring}/package.json
48
+ * The single host-provided framework package. As of 2026-06-22 the framework is
49
+ * one bundle: @camstack/system (kernel + core; types/sdk inlined at runtime,
50
+ * shm-ring is a private native dep). There is no multi-entry allowlist.
54
51
  */
55
- exports.FRAMEWORK_PACKAGE_ALLOWLIST = [
56
- '@camstack/types',
57
- '@camstack/kernel',
58
- '@camstack/core',
59
- '@camstack/sdk',
60
- '@camstack/ui-library',
61
- // Phase 5 / D9 — the cross-platform shared-memory frame plane. A system
62
- // package (native N-API module + `FrameRing`) the stream-broker / decoder /
63
- // frame consumers all depend on; it can't be uninstalled.
64
- '@camstack/shm-ring',
65
- ];
66
- /** Test-only: exported for spec parity check against the manifest. */
52
+ exports.SYSTEM_PACKAGE = '@camstack/system';
53
+ /** Returns true only for the single host-provided framework package. */
67
54
  function isFrameworkPackage(packageName) {
68
- return exports.FRAMEWORK_PACKAGE_ALLOWLIST.includes(packageName);
55
+ return packageName === exports.SYSTEM_PACKAGE;
69
56
  }
70
57
  // ---------------------------------------------------------------------------
71
58
  // Typed JSON helpers
@@ -111,7 +98,7 @@ function asRecord(value) {
111
98
  // Constants
112
99
  // ---------------------------------------------------------------------------
113
100
  /** Core packages that live in the server's own node_modules */
114
- const CORE_MANAGED_PACKAGES = ['@camstack/core', '@camstack/types'];
101
+ const CORE_MANAGED_PACKAGES = ['@camstack/system', '@camstack/types'];
115
102
  class AddonPackageService {
116
103
  loggingService;
117
104
  eventBusService;
@@ -120,7 +107,7 @@ class AddonPackageService {
120
107
  notificationService;
121
108
  toastService;
122
109
  logger;
123
- /** AddonInstaller from @camstack/kernel (may be null if kernel unavailable) */
110
+ /** AddonInstaller from @camstack/system (may be null if kernel unavailable) */
124
111
  installer = null;
125
112
  /** AddonLoader for reloadPackages (may be null) */
126
113
  loader = null;
@@ -155,9 +142,8 @@ class AddonPackageService {
155
142
  // Initialize installer eagerly (no async needed).
156
143
  // Ensures install/uninstall works before full module init completes.
157
144
  try {
158
- const dataDir = process.env.CAMSTACK_DATA ?? 'camstack-data';
159
- const addonsDir = path.resolve(dataDir, 'addons');
160
- const workspacePackagesDir = (0, kernel_1.detectWorkspacePackagesDir)(__dirname);
145
+ const addonsDir = this.resolveAddonsDir();
146
+ const workspacePackagesDir = (0, system_1.detectWorkspacePackagesDir)(__dirname);
161
147
  // `CAMSTACK_NPM_REGISTRY` override exists primarily for the e2e
162
148
  // harness — it points the installer at a sandboxed verdaccio so
163
149
  // the CLI-vs-npm version-interaction tests don't hit the public
@@ -166,12 +152,12 @@ class AddonPackageService {
166
152
  // AddonInstaller). Both the `npm install` shell-out AND the
167
153
  // metadata HTTPS fetch honour this value.
168
154
  const registry = process.env['CAMSTACK_NPM_REGISTRY'];
169
- this.installer = new kernel_1.AddonInstaller({
155
+ this.installer = new system_1.AddonInstaller({
170
156
  addonsDir,
171
157
  workspacePackagesDir: workspacePackagesDir ?? undefined,
172
158
  ...(registry ? { registry } : {}),
173
159
  });
174
- (0, kernel_1.ensureDir)(addonsDir);
160
+ (0, system_1.ensureDir)(addonsDir);
175
161
  }
176
162
  catch (error) {
177
163
  const msg = (0, types_1.errMsg)(error);
@@ -480,7 +466,7 @@ class AddonPackageService {
480
466
  /** List all installed addon packages */
481
467
  listInstalled() {
482
468
  if (!this.installer) {
483
- throw new Error('AddonInstaller is not available — cannot list installed packages. Ensure @camstack/kernel is installed and the addons directory is accessible');
469
+ throw new Error('AddonInstaller is not available — cannot list installed packages. Ensure @camstack/system is installed and the addons directory is accessible');
484
470
  }
485
471
  return this.installer.listInstalled();
486
472
  }
@@ -494,14 +480,14 @@ class AddonPackageService {
494
480
  *
495
481
  * Fallback: if the registry hasn't loaded yet (early-boot install
496
482
  * paths, tests with stub registries), fall back to the legacy list
497
- * so we don't accidentally allow uninstalling `@camstack/core` etc.
483
+ * so we don't accidentally allow uninstalling `@camstack/system` etc.
498
484
  * during a window where protection metadata isn't yet readable.
499
485
  */
500
486
  isProtected(name) {
501
487
  const fromRegistry = this.addonRegistry?.isPackageProtected?.(name);
502
488
  if (typeof fromRegistry === 'boolean')
503
489
  return fromRegistry;
504
- return kernel_1.AddonInstaller.REQUIRED_PACKAGES.includes(name);
490
+ return system_1.AddonInstaller.REQUIRED_PACKAGES.includes(name);
505
491
  }
506
492
  /**
507
493
  * Set of package names with a pending pre-update backup on disk —
@@ -804,7 +790,7 @@ class AddonPackageService {
804
790
  // hub/node_modules/@camstack/ — addons resolve everything from
805
791
  // their own inlined deps.
806
792
  const addonsDir = this.resolveAddonsDir();
807
- const { installPackageFromNpmSync } = await Promise.resolve().then(() => __importStar(require('@camstack/kernel')));
793
+ const { installPackageFromNpmSync } = await Promise.resolve().then(() => __importStar(require('@camstack/system')));
808
794
  const dirName = name.replace(/^@camstack\//, '');
809
795
  const targetDir = path.join(addonsDir, dirName);
810
796
  const packageSpec = version ? `${name}@${version}` : name;
@@ -843,7 +829,7 @@ class AddonPackageService {
843
829
  ...(requestedBy !== undefined ? { requestedBy } : {}),
844
830
  };
845
831
  try {
846
- (0, kernel_1.writePendingRestart)(this.resolveDataDir(), payload);
832
+ (0, system_1.writePendingRestart)(this.resolveDataDir(), payload);
847
833
  }
848
834
  catch (err) {
849
835
  // Marker write failure shouldn't block the restart — log + continue.
@@ -859,7 +845,7 @@ class AddonPackageService {
859
845
  data: payload,
860
846
  });
861
847
  // 10s grace gives in-flight requests time to drain.
862
- (0, kernel_1.scheduleSelfRestart)({ delayMs: 10_000 });
848
+ (0, system_1.scheduleSelfRestart)({ delayMs: 10_000 });
863
849
  }
864
850
  // =========================================================================
865
851
  // Framework live-update
@@ -878,7 +864,7 @@ class AddonPackageService {
878
864
  */
879
865
  async listFrameworkPackages() {
880
866
  const registry = process.env['CAMSTACK_NPM_REGISTRY'];
881
- const rows = await Promise.all(exports.FRAMEWORK_PACKAGE_ALLOWLIST.map(async (packageName) => {
867
+ const rows = await Promise.all([exports.SYSTEM_PACKAGE].map(async (packageName) => {
882
868
  const manifest = readResolvedPackageManifest(packageName);
883
869
  const currentVersion = manifest !== null && typeof manifest['version'] === 'string'
884
870
  ? manifest['version']
@@ -932,8 +918,8 @@ class AddonPackageService {
932
918
  */
933
919
  async updateFrameworkPackage(input) {
934
920
  const { packageName } = input;
935
- if (!exports.FRAMEWORK_PACKAGE_ALLOWLIST.includes(packageName)) {
936
- throw new Error(`updateFrameworkPackage: '${packageName}' is not a framework package. Allowed: ${exports.FRAMEWORK_PACKAGE_ALLOWLIST.join(', ')}`);
921
+ if (packageName !== exports.SYSTEM_PACKAGE) {
922
+ throw new Error(`updateFrameworkPackage: '${packageName}' is not a framework package. Allowed: ${exports.SYSTEM_PACKAGE}`);
937
923
  }
938
924
  const appRoot = resolveFrameworkPackageAppRoot(packageName, this.logger);
939
925
  const fromManifest = readResolvedPackageManifest(packageName);
@@ -971,7 +957,7 @@ class AddonPackageService {
971
957
  ...(input.requestedBy !== undefined ? { requestedBy: input.requestedBy } : {}),
972
958
  };
973
959
  try {
974
- (0, kernel_1.writePendingRestart)(this.resolveDataDir(), markerPayload);
960
+ (0, system_1.writePendingRestart)(this.resolveDataDir(), markerPayload);
975
961
  }
976
962
  catch (err) {
977
963
  // The npm install already completed — the restart will still
@@ -987,7 +973,7 @@ class AddonPackageService {
987
973
  category: types_1.EventCategory.SystemRestarting,
988
974
  data: markerPayload,
989
975
  });
990
- (0, kernel_1.scheduleSelfRestart)({ delayMs: 500 });
976
+ (0, system_1.scheduleSelfRestart)({ delayMs: 500 });
991
977
  return { packageName, fromVersion, toVersion, restartingAt };
992
978
  }
993
979
  // =========================================================================
@@ -996,10 +982,9 @@ class AddonPackageService {
996
982
  /** Re-discover addons from the addons directory (call after install/uninstall) */
997
983
  async reloadPackages() {
998
984
  try {
999
- const kernel = await Promise.resolve().then(() => __importStar(require('@camstack/kernel')));
985
+ const kernel = await Promise.resolve().then(() => __importStar(require('@camstack/system')));
1000
986
  this.loader = new kernel.AddonLoader(this.logger.child('AddonLoader'));
1001
- const dataDir = process.env.CAMSTACK_DATA ?? 'camstack-data';
1002
- const addonsDir = path.resolve(dataDir, 'addons');
987
+ const addonsDir = this.resolveAddonsDir();
1003
988
  await this.loader.loadFromDirectory(addonsDir);
1004
989
  this.logger.info('Reloaded', { meta: { count: this.loader.listAddons().length } });
1005
990
  }
@@ -1402,13 +1387,22 @@ class AddonPackageService {
1402
1387
  }
1403
1388
  /** Resolve the addons directory from config or fall back to default */
1404
1389
  resolveAddonsDir() {
1390
+ // Must match where the runtime LOADS addons (launcher.ts +
1391
+ // addon-registry.service.ts honor CAMSTACK_ADDONS_DIR). If this service
1392
+ // installed/reloaded from a different dir than the runtime loads, a
1393
+ // post-install reload re-registers the hub node from an INCOMPLETE set
1394
+ // (registerNode replaces the cap set atomically) — dropping in-process
1395
+ // builtins like device-manager/addon-pages/addon-widgets.
1396
+ const envDir = process.env.CAMSTACK_ADDONS_DIR;
1397
+ if (envDir && envDir.trim().length > 0)
1398
+ return path.resolve(envDir);
1405
1399
  const dataPath = this.configService.get('server.dataPath') ?? 'camstack-data';
1406
1400
  return path.resolve(dataPath, 'addons');
1407
1401
  }
1408
1402
  /** Throw if installer was not initialised */
1409
1403
  requireInstaller() {
1410
1404
  if (!this.installer) {
1411
- throw new Error('AddonInstaller is not available -- @camstack/kernel may not be installed');
1405
+ throw new Error('AddonInstaller is not available -- @camstack/system may not be installed');
1412
1406
  }
1413
1407
  }
1414
1408
  /** Send notification and toast for a successful package update */