@camstack/system 1.0.2
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.
- package/dist/addon/addon-api-factory.d.ts +35 -0
- package/dist/addon-routes/addon-route-registry.d.ts +37 -0
- package/dist/addon-runner.js +599 -0
- package/dist/addon-runner.mjs +597 -0
- package/dist/auth/api-key-manager.d.ts +26 -0
- package/dist/auth/auth-manager.d.ts +109 -0
- package/dist/auth/parse-record.d.ts +18 -0
- package/dist/auth/scope-matcher.d.ts +7 -0
- package/dist/auth/scoped-token-manager.d.ts +40 -0
- package/dist/auth/totp-manager.d.ts +51 -0
- package/dist/auth/user-manager.d.ts +34 -0
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.d.ts +53 -0
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +259 -0
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +251 -0
- package/dist/builtins/addon-pages-aggregator/dedupe-pages.d.ts +6 -0
- package/dist/builtins/addon-pages-aggregator/index.d.ts +1 -0
- package/dist/builtins/addon-pages-aggregator/index.js +8 -0
- package/dist/builtins/addon-pages-aggregator/index.mjs +2 -0
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.d.ts +47 -0
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +228 -0
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +220 -0
- package/dist/builtins/addon-widgets-aggregator/index.d.ts +1 -0
- package/dist/builtins/addon-widgets-aggregator/index.js +8 -0
- package/dist/builtins/addon-widgets-aggregator/index.mjs +2 -0
- package/dist/builtins/alerts/alerts.addon.d.ts +81 -0
- package/dist/builtins/alerts/alerts.addon.js +601 -0
- package/dist/builtins/alerts/alerts.addon.mjs +595 -0
- package/dist/builtins/alerts/index.d.ts +1 -0
- package/dist/builtins/alerts/index.js +4 -0
- package/dist/builtins/alerts/index.mjs +2 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts +147 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2229 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2220 -0
- package/dist/builtins/backup-orchestrator/cron-helpers.d.ts +23 -0
- package/dist/builtins/backup-orchestrator/destination-policy.d.ts +72 -0
- package/dist/builtins/backup-orchestrator/download-helpers.d.ts +12 -0
- package/dist/builtins/backup-orchestrator/index.d.ts +2 -0
- package/dist/builtins/backup-orchestrator/index.js +8 -0
- package/dist/builtins/backup-orchestrator/index.mjs +2 -0
- package/dist/builtins/backup-orchestrator/manifest-store.d.ts +77 -0
- package/dist/builtins/console-logging/console-destination.d.ts +13 -0
- package/dist/builtins/console-logging/console-logging.addon.d.ts +25 -0
- package/dist/builtins/console-logging/index.d.ts +3 -0
- package/dist/builtins/console-logging/index.js +104 -0
- package/dist/builtins/console-logging/index.mjs +95 -0
- package/dist/builtins/device-manager/device-config-contribution.d.ts +32 -0
- package/dist/builtins/device-manager/device-event-propagator.d.ts +26 -0
- package/dist/builtins/device-manager/device-link-overlay.d.ts +23 -0
- package/dist/builtins/device-manager/device-link-resolver.d.ts +15 -0
- package/dist/builtins/device-manager/device-manager.addon.d.ts +452 -0
- package/dist/builtins/device-manager/device-manager.addon.js +3299 -0
- package/dist/builtins/device-manager/device-manager.addon.mjs +3292 -0
- package/dist/builtins/device-manager/index.d.ts +2 -0
- package/dist/builtins/device-manager/index.js +8 -0
- package/dist/builtins/device-manager/index.mjs +2 -0
- package/dist/builtins/hub-forwarder/hub-forwarder-destination.d.ts +44 -0
- package/dist/builtins/hub-forwarder/hub-forwarder.addon.d.ts +15 -0
- package/dist/builtins/hub-forwarder/index.d.ts +3 -0
- package/dist/builtins/hub-forwarder/index.js +154 -0
- package/dist/builtins/hub-forwarder/index.mjs +145 -0
- package/dist/builtins/local-auth/auth-schema.d.ts +26 -0
- package/dist/builtins/local-auth/index.d.ts +1 -0
- package/dist/builtins/local-auth/index.js +4 -0
- package/dist/builtins/local-auth/index.mjs +2 -0
- package/dist/builtins/local-auth/local-auth.addon.d.ts +18 -0
- package/dist/builtins/local-auth/local-auth.addon.js +8094 -0
- package/dist/builtins/local-auth/local-auth.addon.mjs +8063 -0
- package/dist/builtins/local-auth/oauth-grants.d.ts +45 -0
- package/dist/builtins/local-auth/oauth-session-manager.d.ts +50 -0
- package/dist/builtins/local-network/index.d.ts +2 -0
- package/dist/builtins/local-network/index.js +10 -0
- package/dist/builtins/local-network/index.mjs +2 -0
- package/dist/builtins/local-network/local-network.addon.d.ts +150 -0
- package/dist/builtins/local-network/local-network.addon.js +489 -0
- package/dist/builtins/local-network/local-network.addon.mjs +477 -0
- package/dist/builtins/native-metrics/index.d.ts +2 -0
- package/dist/builtins/native-metrics/native-metrics-provider.d.ts +48 -0
- package/dist/builtins/native-metrics/native-metrics.addon.d.ts +73 -0
- package/dist/builtins/native-metrics/native-metrics.addon.js +922 -0
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +914 -0
- package/dist/builtins/platform-probe/hardware-decode-accel-probe.d.ts +37 -0
- package/dist/builtins/platform-probe/hardware-encoder-probe.d.ts +13 -0
- package/dist/builtins/platform-probe/index.d.ts +22 -0
- package/dist/builtins/platform-probe/index.js +834 -0
- package/dist/builtins/platform-probe/index.mjs +822 -0
- package/dist/builtins/platform-probe/inference-config-resolver.d.ts +29 -0
- package/dist/builtins/platform-probe/intel-accelerators.d.ts +11 -0
- package/dist/builtins/platform-probe/platform-scorer.d.ts +30 -0
- package/dist/builtins/platform-probe/runtime-packages.d.ts +6 -0
- package/dist/builtins/remote-access-orchestrator/enabled-providers-reconcile.d.ts +96 -0
- package/dist/builtins/remote-access-orchestrator/index.d.ts +1 -0
- package/dist/builtins/remote-access-orchestrator/index.js +8 -0
- package/dist/builtins/remote-access-orchestrator/index.mjs +2 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.d.ts +40 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +214 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +208 -0
- package/dist/builtins/shared/settle-sources.d.ts +22 -0
- package/dist/builtins/snapshot/index.d.ts +2 -0
- package/dist/builtins/snapshot/index.js +494 -0
- package/dist/builtins/snapshot/index.mjs +488 -0
- package/dist/builtins/snapshot/snapshot.addon.d.ts +120 -0
- package/dist/builtins/sqlite-storage/config-store.d.ts +8 -0
- package/dist/builtins/sqlite-storage/device-store.d.ts +23 -0
- package/dist/builtins/sqlite-storage/filesystem-browse-provider.d.ts +25 -0
- package/dist/builtins/sqlite-storage/filesystem-storage-provider.d.ts +83 -0
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.d.ts +32 -0
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +396 -0
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +388 -0
- package/dist/builtins/sqlite-storage/index.d.ts +8 -0
- package/dist/builtins/sqlite-storage/index.js +62 -0
- package/dist/builtins/sqlite-storage/index.mjs +49 -0
- package/dist/builtins/sqlite-storage/integration-registry.d.ts +27 -0
- package/dist/builtins/sqlite-storage/path-guard.d.ts +4 -0
- package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +102 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +14 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +644 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +636 -0
- package/dist/builtins/storage-orchestrator/index.d.ts +6 -0
- package/dist/builtins/storage-orchestrator/index.js +10 -0
- package/dist/builtins/storage-orchestrator/index.mjs +2 -0
- package/dist/builtins/storage-orchestrator/location-store.d.ts +49 -0
- package/dist/builtins/storage-orchestrator/provider-discovery.d.ts +10 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +103 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1138 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1128 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +236 -0
- package/dist/builtins/storage-orchestrator/storage-pressure-manager.d.ts +38 -0
- package/dist/builtins/system-backup/system-backup.service.d.ts +137 -0
- package/dist/builtins/system-config/index.d.ts +1 -0
- package/dist/builtins/system-config/index.js +8 -0
- package/dist/builtins/system-config/index.mjs +2 -0
- package/dist/builtins/system-config/system-config.addon.d.ts +10 -0
- package/dist/builtins/system-config/system-config.addon.js +232 -0
- package/dist/builtins/system-config/system-config.addon.mjs +226 -0
- package/dist/builtins/winston-logging/index.d.ts +3 -0
- package/dist/builtins/winston-logging/index.js +156 -0
- package/dist/builtins/winston-logging/index.mjs +144 -0
- package/dist/builtins/winston-logging/winston-destination.d.ts +21 -0
- package/dist/builtins/winston-logging/winston-logging.addon.d.ts +19 -0
- package/dist/chunk-CNf5ZN-e.mjs +37 -0
- package/dist/chunk-Cek0wNdY.js +64 -0
- package/dist/download/model-download-service.d.ts +41 -0
- package/dist/download/model-downloader.d.ts +31 -0
- package/dist/events/event-bus.d.ts +10 -0
- package/dist/events/system-event-bus.d.ts +14 -0
- package/dist/feature/feature-manager.d.ts +11 -0
- package/dist/formatter-B7qW8bPJ.mjs +162 -0
- package/dist/formatter-DqAKDlvN.js +167 -0
- package/dist/http/authenticated-file-server.d.ts +53 -0
- package/dist/http/data-plane-registry.d.ts +23 -0
- package/dist/http/file-data-plane.d.ts +10 -0
- package/dist/http/reverse-proxy.d.ts +15 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.js +93485 -0
- package/dist/index.mjs +93179 -0
- package/dist/intel-accelerators-Gg0P5mnl.js +20 -0
- package/dist/intel-accelerators-hGgpZ0pX.mjs +19 -0
- package/dist/kernel/addon-class-resolver.d.ts +4 -0
- package/dist/kernel/addon-engine-manager.d.ts +22 -0
- package/dist/kernel/addon-health-monitor.d.ts +154 -0
- package/dist/kernel/addon-installer.d.ts +208 -0
- package/dist/kernel/addon-loader.d.ts +106 -0
- package/dist/kernel/addon-manifest.d.ts +77 -0
- package/dist/kernel/capability-handle.d.ts +46 -0
- package/dist/kernel/capability-registry.d.ts +412 -0
- package/dist/kernel/config-manager.d.ts +212 -0
- package/dist/kernel/config-schema.d.ts +93 -0
- package/dist/kernel/custom-action-registry.d.ts +23 -0
- package/dist/kernel/deps/addon-deps-manager.d.ts +19 -0
- package/dist/kernel/deps/manifest-native-deps.d.ts +25 -0
- package/dist/kernel/deps/manifest-python-deps.d.ts +20 -0
- package/dist/kernel/device-registry.d.ts +29 -0
- package/dist/kernel/fs-utils.d.ts +41 -0
- package/dist/kernel/hwaccel/hwaccel-resolver.d.ts +19 -0
- package/dist/kernel/hwaccel/hwaccel-service.d.ts +4 -0
- package/dist/kernel/index.d.ts +74 -0
- package/dist/kernel/infra-capabilities.d.ts +13 -0
- package/dist/kernel/moleculer/addon-context-factory.d.ts +91 -0
- package/dist/kernel/moleculer/addon-data-plane-facility.d.ts +19 -0
- package/dist/kernel/moleculer/addon-runner.d.ts +1 -0
- package/dist/kernel/moleculer/addon-service-factory.d.ts +50 -0
- package/dist/kernel/moleculer/broker-factory.d.ts +50 -0
- package/dist/kernel/moleculer/cap-usage-registry.d.ts +46 -0
- package/dist/kernel/moleculer/capabilities-access.d.ts +21 -0
- package/dist/kernel/moleculer/child-addon-call-dispatch.d.ts +46 -0
- package/dist/kernel/moleculer/child-cap-dispatch.d.ts +20 -0
- package/dist/kernel/moleculer/cluster-secret.d.ts +15 -0
- package/dist/kernel/moleculer/core-cap-service.d.ts +50 -0
- package/dist/kernel/moleculer/crash-supervisor.d.ts +50 -0
- package/dist/kernel/moleculer/device-cap-proxy.d.ts +79 -0
- package/dist/kernel/moleculer/event-bus-core.d.ts +53 -0
- package/dist/kernel/moleculer/event-bus.d.ts +53 -0
- package/dist/kernel/moleculer/hub-log-forwarder.d.ts +36 -0
- package/dist/kernel/moleculer/hub-service.d.ts +35 -0
- package/dist/kernel/moleculer/node-registry.d.ts +126 -0
- package/dist/kernel/moleculer/process-context.d.ts +4 -0
- package/dist/kernel/moleculer/process-service.d.ts +72 -0
- package/dist/kernel/moleculer/provider-registry.d.ts +28 -0
- package/dist/kernel/moleculer/readiness-context.d.ts +62 -0
- package/dist/kernel/moleculer/readiness-service.d.ts +7 -0
- package/dist/kernel/moleculer/register-node-client.d.ts +35 -0
- package/dist/kernel/moleculer/remote-logger.d.ts +43 -0
- package/dist/kernel/moleculer/resilient-cap-call.d.ts +28 -0
- package/dist/kernel/moleculer/stream-probe-service.d.ts +9 -0
- package/dist/kernel/moleculer/trpc-links.d.ts +189 -0
- package/dist/kernel/moleculer/typed-array-serde.d.ts +25 -0
- package/dist/kernel/moleculer/worker-device-restore.d.ts +10 -0
- package/dist/kernel/provider-kind-drift.d.ts +12 -0
- package/dist/kernel/restart-coordinator.d.ts +90 -0
- package/dist/kernel/storage-location-registry.d.ts +40 -0
- package/dist/kernel/transport/cap-action-name.d.ts +100 -0
- package/dist/kernel/transport/cap-route-resolver.d.ts +148 -0
- package/dist/kernel/transport/cap-route.d.ts +148 -0
- package/dist/kernel/transport/child-cap-protocol.d.ts +136 -0
- package/dist/kernel/transport/create-local-transport.d.ts +7 -0
- package/dist/kernel/transport/frame-codec.d.ts +7 -0
- package/dist/kernel/transport/index.d.ts +27 -0
- package/dist/kernel/transport/local-child-client.d.ts +136 -0
- package/dist/kernel/transport/local-child-registry.d.ts +179 -0
- package/dist/kernel/transport/local-endpoint-path.d.ts +6 -0
- package/dist/kernel/transport/local-transport.d.ts +46 -0
- package/dist/kernel/transport/parent-unowned-call.d.ts +75 -0
- package/dist/kernel/transport/socket-channel.d.ts +27 -0
- package/dist/kernel/transport/uds-event-bridge.d.ts +36 -0
- package/dist/kernel/transport/uds-event-bus.d.ts +22 -0
- package/dist/kernel/transport/uds-local-transport.d.ts +18 -0
- package/dist/kernel/transport/uds-log-ingest.d.ts +28 -0
- package/dist/kernel/transport/uds-logger.d.ts +44 -0
- package/dist/kernel/utils/ring-buffer.d.ts +15 -0
- package/dist/kernel/workspace-detect.d.ts +9 -0
- package/dist/lifecycle/lifecycle-state-machine.d.ts +28 -0
- package/dist/logging/formatter.d.ts +30 -0
- package/dist/logging/log-manager.d.ts +54 -0
- package/dist/logging/log-ring-buffer.d.ts +47 -0
- package/dist/logging/partitioned-log-buffer.d.ts +35 -0
- package/dist/logging/scoped-logger.d.ts +17 -0
- package/dist/main-DNnMW7Z2.js +9983 -0
- package/dist/main-rtjOwPBR.mjs +9976 -0
- package/dist/manifest-python-deps-D1DbAQEv.js +6724 -0
- package/dist/manifest-python-deps-DZsKTbs1.mjs +6315 -0
- package/dist/network/network-quality.d.ts +11 -0
- package/dist/notification/notification-service.d.ts +37 -0
- package/dist/notification/toast-service.d.ts +22 -0
- package/dist/pipeline/engine-manager-resolver.d.ts +15 -0
- package/dist/pipeline/pipeline-runner.d.ts +8 -0
- package/dist/pipeline/pipeline-validator.d.ts +13 -0
- package/dist/process/resource-monitor.d.ts +11 -0
- package/dist/python/python-env-manager.d.ts +12 -0
- package/dist/repl/interfaces.d.ts +31 -0
- package/dist/repl/repl-engine.d.ts +8 -0
- package/dist/resource-monitor-ClDGFyf6.mjs +57 -0
- package/dist/resource-monitor-IIEanuJt.js +74 -0
- package/dist/settle-sources-Bhsy57y-.js +38 -0
- package/dist/settle-sources-CDtNC8ub.mjs +33 -0
- package/dist/storage/fs-storage-backend.d.ts +40 -0
- package/dist/storage/storage-location-manager.d.ts +23 -0
- package/dist/storage/storage-manager.d.ts +83 -0
- package/dist/tar-BgAEMRBR.js +5434 -0
- package/dist/tar-ByMOPNM0.mjs +5429 -0
- package/dist/tls/cert-manager.d.ts +26 -0
- package/dist/tls/index.d.ts +1 -0
- package/package.json +343 -0
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
import { $ as setWorkerNativeCapsChangeListener, A as createUdsLoggerWithControl, L as LocalChildClient, X as getWorkerNativeCapProvider, Z as getWorkerNativeCapSnapshot, dt as CustomActionRegistry, i as createUdsAddonContext, mt as resolveAddonClass, t as installManifestPythonDeps, tt as validateProviderRegistrations } from "./manifest-python-deps-DZsKTbs1.mjs";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path$1 from "node:path";
|
|
4
|
+
import { ReadinessRegistry, ReadinessTimeoutError, errMsg, normalizeAddonInitResult } from "@camstack/types";
|
|
5
|
+
//#region src/kernel/moleculer/worker-device-restore.ts
|
|
6
|
+
/**
|
|
7
|
+
* Worker-side device restore helper.
|
|
8
|
+
*
|
|
9
|
+
* Shared by `process-runner` (single-addon subprocess) and
|
|
10
|
+
* `group-runner` (group subprocess). Calls the worker addon's
|
|
11
|
+
* `restoreDevices(SavedDevice[])` with devices read from the hub's
|
|
12
|
+
* `device-manager` capability. Blocks on the `system.ready-state`
|
|
13
|
+
* readiness of the hub's `device-manager` via
|
|
14
|
+
* `ReadinessRegistry.awaitReady`, then issues the calls ONCE.
|
|
15
|
+
*
|
|
16
|
+
* Lives in its own module so it can be imported without triggering
|
|
17
|
+
* the runner entry-point side effects (each runner is also an
|
|
18
|
+
* executable that calls `main()` at import time).
|
|
19
|
+
*/
|
|
20
|
+
async function runWorkerDeviceRestoreWithRetry(addon, context, addonId, sourceNodeId) {
|
|
21
|
+
const log = context.logger;
|
|
22
|
+
log?.debug?.(`[worker-restore] entry: addon="${addonId}" sourceNodeId="${sourceNodeId}"`);
|
|
23
|
+
const restoreFn = addon.restoreDevices;
|
|
24
|
+
if (typeof restoreFn !== "function") {
|
|
25
|
+
log?.warn?.(`[worker-restore] "${addonId}": no restoreDevices function — skipping`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const api = context.api;
|
|
29
|
+
if (!api) {
|
|
30
|
+
log?.warn?.(`[worker-restore] "${addonId}": context.api missing — skipping`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const bus = context.eventBus;
|
|
34
|
+
if (!bus) {
|
|
35
|
+
log?.warn?.(`[worker-restore] "${addonId}": context.eventBus missing — skipping`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const shared = context.kernel?.readinessRegistry ?? null;
|
|
39
|
+
const registry = shared ?? new ReadinessRegistry({
|
|
40
|
+
eventBus: bus,
|
|
41
|
+
sourceNodeId,
|
|
42
|
+
logger: context.logger
|
|
43
|
+
});
|
|
44
|
+
log?.debug?.(`[worker-restore] "${addonId}": awaiting device-manager readiness on hub...`);
|
|
45
|
+
try {
|
|
46
|
+
await registry.awaitReady("device-manager", {
|
|
47
|
+
type: "node",
|
|
48
|
+
nodeId: "hub"
|
|
49
|
+
}, { timeoutMs: 6e4 });
|
|
50
|
+
log?.debug?.(`[worker-restore] "${addonId}": device-manager READY`);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if (err instanceof ReadinessTimeoutError) {
|
|
53
|
+
context.logger?.warn?.(`[worker-restore] device-manager not ready within ${err.waitedMs}ms — skipping for "${addonId}"`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
throw err;
|
|
57
|
+
} finally {
|
|
58
|
+
if (!shared && registry instanceof ReadinessRegistry) registry.close();
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const deviceManager = Reflect.get(api, "deviceManager");
|
|
62
|
+
log?.debug?.(`[worker-restore] "${addonId}": calling listPersistedByAddon...`);
|
|
63
|
+
const rowsResult = await deviceManager.listPersistedByAddon.query({ addonId });
|
|
64
|
+
log?.debug?.(`[worker-restore] "${addonId}": got ${Array.isArray(rowsResult) ? rowsResult.length : 0} row(s)`);
|
|
65
|
+
const rows = Array.isArray(rowsResult) ? rowsResult : [];
|
|
66
|
+
if (rows.length === 0) {
|
|
67
|
+
context.logger?.info?.(`[worker-restore] no persisted devices to restore for addon "${addonId}"`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const savedDevices = await Promise.all(rows.map(async (row) => {
|
|
71
|
+
const config = await deviceManager.loadConfig.query({ deviceId: row.id });
|
|
72
|
+
return {
|
|
73
|
+
id: row.id,
|
|
74
|
+
stableId: row.stableId,
|
|
75
|
+
type: row.type,
|
|
76
|
+
name: row.name,
|
|
77
|
+
parentDeviceId: row.parentDeviceId,
|
|
78
|
+
config: config ?? {}
|
|
79
|
+
};
|
|
80
|
+
}));
|
|
81
|
+
await restoreFn.call(addon, savedDevices);
|
|
82
|
+
log?.info?.(`[worker-restore] "${addonId}": restored ${savedDevices.length} device(s)`);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
log?.warn?.(`[worker-restore] "${addonId}": restoreDevices threw: ${errMsg(err)}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/kernel/moleculer/child-cap-dispatch.ts
|
|
89
|
+
/** Type guard narrowing a dynamically-read provider member to a callable method. */
|
|
90
|
+
function isCapMethod(value) {
|
|
91
|
+
return typeof value === "function";
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Creates a unified dispatcher that routes a {@link CapCallInput} to either a
|
|
95
|
+
* device-scoped or singleton provider, then invokes the requested method.
|
|
96
|
+
*
|
|
97
|
+
* Pure module — no Moleculer, no broker, no addon-runner dependency.
|
|
98
|
+
*/
|
|
99
|
+
function createChildCapDispatch(deps) {
|
|
100
|
+
return async (call) => {
|
|
101
|
+
const { capName, method, args, deviceId } = call;
|
|
102
|
+
let provider;
|
|
103
|
+
if (deviceId !== void 0) {
|
|
104
|
+
provider = deps.getDeviceProvider(capName, deviceId) ?? deps.getSingletonProvider(capName);
|
|
105
|
+
if (provider == null) throw new Error(`child-cap-dispatch: no provider for '${capName}' (device '${String(deviceId)}' or singleton)`);
|
|
106
|
+
} else {
|
|
107
|
+
provider = deps.getSingletonProvider(capName);
|
|
108
|
+
if (provider == null) throw new Error(`child-cap-dispatch: no singleton provider for '${capName}'`);
|
|
109
|
+
}
|
|
110
|
+
const fn = Reflect.get(provider, method);
|
|
111
|
+
if (!isCapMethod(fn)) throw new Error(`child-cap-dispatch: method '${method}' not found on provider '${capName}'`);
|
|
112
|
+
return await fn.call(provider, args);
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/kernel/moleculer/child-addon-call-dispatch.ts
|
|
117
|
+
function isRecord$1(value) {
|
|
118
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Strip the (non-serializable) `handler` from a route descriptor, keeping only
|
|
122
|
+
* the keys the hub needs to mount + describe the route. The hub bridges actual
|
|
123
|
+
* dispatch through the `addon-routes` cap's `invoke` method, so the handler
|
|
124
|
+
* never needs to cross the wire.
|
|
125
|
+
*/
|
|
126
|
+
function toSerializableRoute(route) {
|
|
127
|
+
if (!isRecord$1(route)) throw new Error("child-addon-call: getRoutes() returned a non-object route descriptor");
|
|
128
|
+
const method = route["method"];
|
|
129
|
+
const path = route["path"];
|
|
130
|
+
if (typeof method !== "string" || typeof path !== "string") throw new Error("child-addon-call: route descriptor missing method/path");
|
|
131
|
+
const access = route["access"];
|
|
132
|
+
const description = route["description"];
|
|
133
|
+
return {
|
|
134
|
+
method,
|
|
135
|
+
path,
|
|
136
|
+
...typeof access === "string" ? { access } : {},
|
|
137
|
+
...typeof description === "string" ? { description } : {}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Dispatch a `target: 'settings'` addon-call to the loaded addon instance.
|
|
142
|
+
* Mirrors the (removed) `<addonId>.settings.<method>` Moleculer action. The
|
|
143
|
+
* inner `method` switch is also exhaustive (`never` check) so a new settings
|
|
144
|
+
* method added to `AddonSettingsMethod` must be handled here too. Returns the
|
|
145
|
+
* raw method result; the hub reshapes get-results and acks updates.
|
|
146
|
+
*/
|
|
147
|
+
async function dispatchSettings(addon, addonId, method, args) {
|
|
148
|
+
switch (method) {
|
|
149
|
+
case "getGlobalSettings": return typeof addon.getGlobalSettings === "function" ? await addon.getGlobalSettings(args.overlay, args.cap) ?? null : null;
|
|
150
|
+
case "updateGlobalSettings":
|
|
151
|
+
if (typeof addon.updateGlobalSettings !== "function") throw new Error(`child-addon-call: addon "${addonId}" does not implement updateGlobalSettings`);
|
|
152
|
+
await addon.updateGlobalSettings(args.patch ?? {});
|
|
153
|
+
return { success: true };
|
|
154
|
+
case "getDeviceSettings": return typeof addon.getDeviceSettings === "function" && typeof args.deviceId === "number" ? await addon.getDeviceSettings(args.deviceId) ?? null : null;
|
|
155
|
+
case "updateDeviceSettings":
|
|
156
|
+
if (typeof addon.updateDeviceSettings !== "function") throw new Error(`child-addon-call: addon "${addonId}" does not implement updateDeviceSettings`);
|
|
157
|
+
if (typeof args.deviceId !== "number") throw new Error(`child-addon-call: addon "${addonId}" updateDeviceSettings missing deviceId`);
|
|
158
|
+
await addon.updateDeviceSettings(args.deviceId, args.patch ?? {});
|
|
159
|
+
return { success: true };
|
|
160
|
+
default: throw new Error(`child-addon-call: unhandled settings method "${String(method)}"`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Creates the addon-level dispatcher the addon-runner wires to
|
|
165
|
+
* `LocalChildClient.onAddonCall`. Routes a {@link AddonCallInput} to one of the
|
|
166
|
+
* exhaustive {@link AddonCallTarget} surfaces (routes / custom / settings) —
|
|
167
|
+
* everything the per-addon Moleculer broker used to carry before the UDS
|
|
168
|
+
* migration removed it. The `target` switch is exhaustive (`never` check): a
|
|
169
|
+
* new surface added to the protocol union that isn't dispatched here is a
|
|
170
|
+
* COMPILE ERROR — the guard that makes a "missed link" impossible.
|
|
171
|
+
*
|
|
172
|
+
* Pure module — no Moleculer, no broker, no addon-runner dependency.
|
|
173
|
+
*/
|
|
174
|
+
function createChildAddonCallDispatch(deps) {
|
|
175
|
+
return async (call) => {
|
|
176
|
+
const { addonId, target } = call;
|
|
177
|
+
switch (target) {
|
|
178
|
+
case "routes": {
|
|
179
|
+
const provider = deps.getRouteProvider(addonId);
|
|
180
|
+
if (provider == null) throw new Error(`child-addon-call: addon "${addonId}" has no addon-routes provider`);
|
|
181
|
+
const getRoutes = Reflect.get(provider, "getRoutes");
|
|
182
|
+
if (typeof getRoutes !== "function") throw new Error(`child-addon-call: addon "${addonId}" addon-routes provider has no getRoutes()`);
|
|
183
|
+
const live = await getRoutes.call(provider);
|
|
184
|
+
if (!Array.isArray(live)) throw new Error(`child-addon-call: addon "${addonId}" getRoutes() did not return an array`);
|
|
185
|
+
return live.map(toSerializableRoute);
|
|
186
|
+
}
|
|
187
|
+
case "custom": {
|
|
188
|
+
const action = call.action;
|
|
189
|
+
if (typeof action !== "string" || action.length === 0) throw new Error(`child-addon-call: addon "${addonId}" custom call missing action name`);
|
|
190
|
+
const entry = deps.customActions.resolve(addonId, action);
|
|
191
|
+
if (entry === null) throw new Error(`child-addon-call: addon "${addonId}" has no custom action "${action}"`);
|
|
192
|
+
return entry.handler(call.args);
|
|
193
|
+
}
|
|
194
|
+
case "settings": {
|
|
195
|
+
const method = call.method;
|
|
196
|
+
if (method == null) throw new Error(`child-addon-call: addon "${addonId}" settings call missing method`);
|
|
197
|
+
const addon = deps.getAddonInstance(addonId);
|
|
198
|
+
if (addon == null) throw new Error(`child-addon-call: addon "${addonId}" not loaded on this node`);
|
|
199
|
+
return dispatchSettings(addon, addonId, method, call.args ?? {});
|
|
200
|
+
}
|
|
201
|
+
case "data-planes": return deps.getDataPlanes(addonId);
|
|
202
|
+
default: throw new Error(`child-addon-call: unhandled addon-call target "${String(target)}"`);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region src/kernel/moleculer/addon-runner.ts
|
|
208
|
+
/**
|
|
209
|
+
* Addon Runner — THE unified entry point for spawned subprocesses.
|
|
210
|
+
*
|
|
211
|
+
* Every spawned process is a size-1 addon set — one addon, one process,
|
|
212
|
+
* zero exceptions (D2/D9). The media-pipeline co-location group was
|
|
213
|
+
* dissolved by Phase 5 once frames and audio chunks became serialisable
|
|
214
|
+
* (shm `FrameHandle`s / tRPC), so no addon declares `execution.group`
|
|
215
|
+
* and `CAMSTACK_RUNNER_ADDONS` always carries a single element. The
|
|
216
|
+
* runner still accepts a multi-element array structurally — that is a
|
|
217
|
+
* mechanism, not a policy; no manifest exercises it.
|
|
218
|
+
*
|
|
219
|
+
* UDS-ONLY (Phase F1+F2 — irreversible). The forked child runs with NO
|
|
220
|
+
* Moleculer `ServiceBroker`. Its only link to the parent (hub or agent)
|
|
221
|
+
* is a `LocalChildClient` over a Unix-domain socket:
|
|
222
|
+
*
|
|
223
|
+
* - Caps: `ctx.api.<cap>.<method>(...)` resolves a same-process
|
|
224
|
+
* sibling via `localProviderLink`; everything else rides
|
|
225
|
+
* `ipcParentLink → client.callOut` → the parent's
|
|
226
|
+
* `onUnownedCall` (resolver-first + broker-fallback) — F0.
|
|
227
|
+
* - Events: `createUdsEventBus` — local `deliverShared` + UDS forward.
|
|
228
|
+
* - Logs: `createUdsLogger` — `client.sendLog` (pre-connect buffered).
|
|
229
|
+
* - Readiness: `getOrInitReadinessRegistryForClient` — hydrate via
|
|
230
|
+
* `client.requestReadinessSnapshot()` on connect.
|
|
231
|
+
* - Registration: the UDS `register`/`updateCaps` message IS the
|
|
232
|
+
* handshake (the parent applies it via `onChildRegistered`,
|
|
233
|
+
* mirroring `$hub.registerNode`). No Moleculer RPC.
|
|
234
|
+
*
|
|
235
|
+
* CRITICAL ORDERING: `udsChildClient.start()` MUST resolve BEFORE any addon
|
|
236
|
+
* module is loaded/initialised — addon `initialize()` issues `ctx.api` /
|
|
237
|
+
* `ctx.settings` calls that route over UDS via `ipcParentLink`, and there is
|
|
238
|
+
* no broker fallback anymore. The client's pre-connect buffers flush on
|
|
239
|
+
* connect, so init-time logs/events are preserved.
|
|
240
|
+
*
|
|
241
|
+
* Environment variables:
|
|
242
|
+
* - CAMSTACK_RUNNER_ID — runner label (becomes the nodeID suffix)
|
|
243
|
+
* - CAMSTACK_RUNNER_ADDONS — JSON array of `{ addonId, addonDir }`
|
|
244
|
+
* - CAMSTACK_PARENT_NODE_ID — hub or agent nodeID
|
|
245
|
+
* - CAMSTACK_PARENT_UDS_PATH — REQUIRED: the parent's UDS endpoint
|
|
246
|
+
* - CAMSTACK_DATA_DIR — addon data root (default ./camstack-data)
|
|
247
|
+
* - CAMSTACK_LOG_LEVEL — log level (default debug)
|
|
248
|
+
*
|
|
249
|
+
* Exits non-zero on fatal init failures so the parent process supervisor
|
|
250
|
+
* can respawn. Graceful SIGTERM/SIGINT triggers `addon.shutdown()` for
|
|
251
|
+
* every loaded addon before closing the UDS channel.
|
|
252
|
+
*/
|
|
253
|
+
process.on("uncaughtException", (err) => {
|
|
254
|
+
console.error(`[addon-runner CRASH] uncaughtException: ${errMsg(err)}\n${err instanceof Error ? err.stack ?? "" : ""}`);
|
|
255
|
+
process.exit(1);
|
|
256
|
+
});
|
|
257
|
+
process.on("unhandledRejection", (reason) => {
|
|
258
|
+
console.error(`[addon-runner CRASH] unhandledRejection: ${errMsg(reason)}\n${reason instanceof Error ? reason.stack ?? "" : ""}`);
|
|
259
|
+
});
|
|
260
|
+
process.on("exit", (code) => {
|
|
261
|
+
console.error(`[addon-runner EXIT] code=${String(code)} nodeId=${process.env["CAMSTACK_RUNNER_ID"] ?? "?"}`);
|
|
262
|
+
});
|
|
263
|
+
for (const sig of [
|
|
264
|
+
"SIGTERM",
|
|
265
|
+
"SIGINT",
|
|
266
|
+
"SIGHUP"
|
|
267
|
+
]) process.on(sig, () => {
|
|
268
|
+
console.error(`[addon-runner SIGNAL] received ${sig} nodeId=${process.env["CAMSTACK_RUNNER_ID"] ?? "?"}`);
|
|
269
|
+
});
|
|
270
|
+
function isRecord(value) {
|
|
271
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
272
|
+
}
|
|
273
|
+
function parseAddonsSpec(raw) {
|
|
274
|
+
if (!raw) return [];
|
|
275
|
+
let parsed;
|
|
276
|
+
try {
|
|
277
|
+
parsed = JSON.parse(raw);
|
|
278
|
+
} catch (err) {
|
|
279
|
+
throw new Error(`[addon-runner] CAMSTACK_RUNNER_ADDONS is not valid JSON: ${errMsg(err)}`, { cause: err });
|
|
280
|
+
}
|
|
281
|
+
if (!Array.isArray(parsed)) throw new Error(`[addon-runner] CAMSTACK_RUNNER_ADDONS must be a JSON array`);
|
|
282
|
+
const specs = [];
|
|
283
|
+
for (const entry of parsed) {
|
|
284
|
+
if (!isRecord(entry)) throw new Error(`[addon-runner] addon spec is not an object: ${JSON.stringify(entry)}`);
|
|
285
|
+
const addonId = entry["addonId"];
|
|
286
|
+
const addonDir = entry["addonDir"];
|
|
287
|
+
if (typeof addonId !== "string" || typeof addonDir !== "string") throw new Error(`[addon-runner] addon spec missing addonId/addonDir: ${JSON.stringify(entry)}`);
|
|
288
|
+
specs.push({
|
|
289
|
+
addonId,
|
|
290
|
+
addonDir
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return specs;
|
|
294
|
+
}
|
|
295
|
+
async function loadAddonModule(addonId, addonDir) {
|
|
296
|
+
const pkgJsonPath = path$1.join(addonDir, "package.json");
|
|
297
|
+
if (!fs.existsSync(pkgJsonPath)) throw new Error(`No package.json at ${addonDir}`);
|
|
298
|
+
const declaration = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"))["camstack"]?.addons?.find((a) => a.id === addonId);
|
|
299
|
+
if (!declaration?.entry) throw new Error(`Addon "${addonId}" not found in ${pkgJsonPath} manifest`);
|
|
300
|
+
const entryPath = path$1.resolve(addonDir, declaration.entry);
|
|
301
|
+
if (!fs.existsSync(entryPath)) throw new Error(`Entry point not found: ${entryPath}`);
|
|
302
|
+
const modUnknown = await import(entryPath);
|
|
303
|
+
const mod = isRecord(modUnknown) ? modUnknown : {};
|
|
304
|
+
const AddonClass = resolveAddonClass(mod);
|
|
305
|
+
if (!AddonClass) throw new Error(`No addon class exported from ${entryPath}`);
|
|
306
|
+
return {
|
|
307
|
+
declaration,
|
|
308
|
+
mod,
|
|
309
|
+
AddonClass
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Builds the {@link ChildCapDescriptor} array that the UDS child transport
|
|
314
|
+
* sends to the parent on registration.
|
|
315
|
+
*
|
|
316
|
+
* - Singleton caps come from the runner UNION manifest (all caps registered by
|
|
317
|
+
* every addon in this runner). One descriptor per unique cap name, mode
|
|
318
|
+
* `'singleton'` and no `deviceId`.
|
|
319
|
+
* - Device-scoped caps come from the native-cap snapshot — one descriptor per
|
|
320
|
+
* (capName, deviceId) pair, mode `'collection'`.
|
|
321
|
+
*/
|
|
322
|
+
function buildChildCapDescriptors(manifest, nativeCapSnapshot) {
|
|
323
|
+
const descriptors = [];
|
|
324
|
+
const seen = /* @__PURE__ */ new Set();
|
|
325
|
+
for (const entry of manifest) for (const capName of entry.capabilities) if (!seen.has(capName)) {
|
|
326
|
+
seen.add(capName);
|
|
327
|
+
descriptors.push({
|
|
328
|
+
capName,
|
|
329
|
+
mode: "singleton"
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
for (const { capName, deviceIds } of nativeCapSnapshot) for (const deviceId of deviceIds) descriptors.push({
|
|
333
|
+
capName,
|
|
334
|
+
mode: "collection",
|
|
335
|
+
deviceId
|
|
336
|
+
});
|
|
337
|
+
return descriptors;
|
|
338
|
+
}
|
|
339
|
+
async function main() {
|
|
340
|
+
const runnerId = process.env["CAMSTACK_RUNNER_ID"];
|
|
341
|
+
const addonsJson = process.env["CAMSTACK_RUNNER_ADDONS"];
|
|
342
|
+
const parentNodeId = process.env["CAMSTACK_PARENT_NODE_ID"];
|
|
343
|
+
const dataDir = process.env["CAMSTACK_DATA_DIR"] ?? "./camstack-data";
|
|
344
|
+
if (!runnerId || !addonsJson || !parentNodeId) {
|
|
345
|
+
console.error("[addon-runner] Missing required env vars: CAMSTACK_RUNNER_ID, CAMSTACK_RUNNER_ADDONS, CAMSTACK_PARENT_NODE_ID");
|
|
346
|
+
process.exit(1);
|
|
347
|
+
}
|
|
348
|
+
let specs;
|
|
349
|
+
try {
|
|
350
|
+
specs = parseAddonsSpec(addonsJson);
|
|
351
|
+
} catch (err) {
|
|
352
|
+
console.error(errMsg(err));
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
if (specs.length === 0) {
|
|
356
|
+
console.error("[addon-runner] CAMSTACK_RUNNER_ADDONS resolved to 0 addons — refusing to start");
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
const nodeId = `${parentNodeId}/${runnerId}`;
|
|
360
|
+
if (!process.env["CAMSTACK_PARENT_UDS_PATH"]) {
|
|
361
|
+
console.error(`[addon-runner] "${nodeId}" CAMSTACK_PARENT_UDS_PATH is unset — broker-less runner cannot reach its parent. Exiting.`);
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
console.log(`[addon-runner] Starting "${nodeId}" (UDS-only) with ${specs.length} addon(s): ${specs.map((s) => s.addonId).join(", ")}`);
|
|
365
|
+
const loadedModules = [];
|
|
366
|
+
for (const spec of specs) try {
|
|
367
|
+
const { declaration, AddonClass } = await loadAddonModule(spec.addonId, spec.addonDir);
|
|
368
|
+
loadedModules.push({
|
|
369
|
+
spec,
|
|
370
|
+
declaration,
|
|
371
|
+
AddonClass
|
|
372
|
+
});
|
|
373
|
+
} catch (err) {
|
|
374
|
+
console.error(`[addon-runner] Failed to load "${spec.addonId}": ${errMsg(err)}`);
|
|
375
|
+
process.exit(1);
|
|
376
|
+
}
|
|
377
|
+
let storageProvider;
|
|
378
|
+
let storageProviderError;
|
|
379
|
+
try {
|
|
380
|
+
const { FilesystemStorageProvider } = await import("@camstack/types/node");
|
|
381
|
+
storageProvider = new FilesystemStorageProvider(dataDir);
|
|
382
|
+
} catch (err) {
|
|
383
|
+
storageProviderError = err;
|
|
384
|
+
}
|
|
385
|
+
const INFRA_CAP_NAMES = new Set([
|
|
386
|
+
"settings-store",
|
|
387
|
+
"storage",
|
|
388
|
+
"log-destination",
|
|
389
|
+
"metrics-provider",
|
|
390
|
+
"platform-probe"
|
|
391
|
+
]);
|
|
392
|
+
const infraPriority = (decl) => {
|
|
393
|
+
const caps = decl.capabilities ?? [];
|
|
394
|
+
for (const cap of caps) {
|
|
395
|
+
const name = typeof cap === "string" ? cap : cap.name;
|
|
396
|
+
if (INFRA_CAP_NAMES.has(name)) return 0;
|
|
397
|
+
}
|
|
398
|
+
return 1;
|
|
399
|
+
};
|
|
400
|
+
loadedModules.sort((a, b) => infraPriority(a.declaration) - infraPriority(b.declaration));
|
|
401
|
+
/** UNION manifest accumulates one entry per addon as each initialises. */
|
|
402
|
+
const runnerManifest = [];
|
|
403
|
+
/**
|
|
404
|
+
* Aggregated singleton (system-scoped) providers across ALL addons in this
|
|
405
|
+
* runner — keyed by capName. Read by the UDS child-cap dispatch to resolve
|
|
406
|
+
* singleton calls (no Moleculer). Populated during the addon-init loop; a
|
|
407
|
+
* cap-call that arrives before its addon initialises is rejected by the
|
|
408
|
+
* dispatcher (the parent only routes caps the child has registered via the
|
|
409
|
+
* UDS `register`/`updateCaps` handshake).
|
|
410
|
+
*/
|
|
411
|
+
const allSingletonProviders = /* @__PURE__ */ new Map();
|
|
412
|
+
/**
|
|
413
|
+
* Per-addon route-provider map — keyed by addonId → the addon's live
|
|
414
|
+
* `addon-routes` provider object. Resolves `getRouteProvider(addonId)`
|
|
415
|
+
* per-addon rather than returning a single global provider. Populated
|
|
416
|
+
* during the init loop alongside `allSingletonProviders`.
|
|
417
|
+
*
|
|
418
|
+
* This is the correct abstraction even in the current single-addon-per-runner
|
|
419
|
+
* policy (D2/D9): it makes the lookup honest and future-safe for any explicit
|
|
420
|
+
* co-location scenario where two addons each declare `addon-routes`.
|
|
421
|
+
*/
|
|
422
|
+
const addonRouteProviders = /* @__PURE__ */ new Map();
|
|
423
|
+
/**
|
|
424
|
+
* Per-addon HTTP data-plane endpoints — keyed by addonId → the descriptors
|
|
425
|
+
* the addon's `ctx.dataPlane` facility published (via the `dataPlaneSink`
|
|
426
|
+
* passed into the context). Read by the UDS `target:'data-planes'` dispatch
|
|
427
|
+
* so the hub can pull them and set up its reverse-proxy. Mirrors
|
|
428
|
+
* `addonRouteProviders`.
|
|
429
|
+
*/
|
|
430
|
+
const addonDataPlanes = /* @__PURE__ */ new Map();
|
|
431
|
+
/**
|
|
432
|
+
* Per-addon instance map — keyed by addonId → the loaded addon instance.
|
|
433
|
+
* Backs the `settings` addon-call surface (`getAddonInstance`), which invokes
|
|
434
|
+
* the addon's global/device settings methods. Populated during the init loop;
|
|
435
|
+
* read lazily at call time (the dispatcher only fires after init completes).
|
|
436
|
+
*/
|
|
437
|
+
const loadedAddonInstances = /* @__PURE__ */ new Map();
|
|
438
|
+
/**
|
|
439
|
+
* Per-addon custom-action registry — populated during the init loop. Read by
|
|
440
|
+
* the UDS addon-call dispatch to resolve `target:'custom'` calls (mirrors the
|
|
441
|
+
* removed `custom.<action>` Moleculer action). Constructed up front so the
|
|
442
|
+
* addon-call handler can close over it at client-construction time.
|
|
443
|
+
*/
|
|
444
|
+
const customActionRegistry = new CustomActionRegistry();
|
|
445
|
+
let shuttingDown = false;
|
|
446
|
+
const udsChildClient = new LocalChildClient({
|
|
447
|
+
nodeId: parentNodeId,
|
|
448
|
+
childId: runnerId,
|
|
449
|
+
caps: buildChildCapDescriptors(runnerManifest, getWorkerNativeCapSnapshot()),
|
|
450
|
+
dispatch: createChildCapDispatch({
|
|
451
|
+
getSingletonProvider: (capName) => allSingletonProviders.get(capName) ?? null,
|
|
452
|
+
getDeviceProvider: (capName, deviceId) => getWorkerNativeCapProvider(capName, deviceId) ?? null
|
|
453
|
+
})
|
|
454
|
+
});
|
|
455
|
+
udsChildClient.onAddonCall(createChildAddonCallDispatch({
|
|
456
|
+
getRouteProvider: (addonId) => addonRouteProviders.get(addonId) ?? null,
|
|
457
|
+
customActions: customActionRegistry,
|
|
458
|
+
getAddonInstance: (addonId) => loadedAddonInstances.get(addonId) ?? null,
|
|
459
|
+
getDataPlanes: (addonId) => addonDataPlanes.get(addonId) ?? []
|
|
460
|
+
}));
|
|
461
|
+
const { logger: runnerLog, control: runnerLogControl } = createUdsLoggerWithControl(udsChildClient, runnerId, nodeId, "addon-runner");
|
|
462
|
+
udsChildClient.onSetLogLevel((level) => {
|
|
463
|
+
runnerLogControl.setLevel(level);
|
|
464
|
+
runnerLog.info(`Log level changed to "${level}" via UDS`);
|
|
465
|
+
});
|
|
466
|
+
try {
|
|
467
|
+
await udsChildClient.start();
|
|
468
|
+
} catch (err) {
|
|
469
|
+
console.error(`[addon-runner] "${nodeId}" UDS connect failed — exiting: ${errMsg(err)}`);
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
472
|
+
runnerLog.info("UDS child transport connected", { meta: { childId: runnerId } });
|
|
473
|
+
if (storageProvider) runnerLog.info(`"${nodeId}" storage provider rooted at ${dataDir}`);
|
|
474
|
+
else runnerLog.warn(`"${nodeId}" failed to load FilesystemStorageProvider`, { meta: { error: errMsg(storageProviderError) } });
|
|
475
|
+
const refreshChildCaps = (reason) => {
|
|
476
|
+
const freshDescriptors = buildChildCapDescriptors(runnerManifest, getWorkerNativeCapSnapshot());
|
|
477
|
+
udsChildClient.updateCaps(freshDescriptors).catch((err) => {
|
|
478
|
+
runnerLog.warn(`UDS updateCaps failed (${reason})`, { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
479
|
+
});
|
|
480
|
+
};
|
|
481
|
+
let capRefreshTimer;
|
|
482
|
+
setWorkerNativeCapsChangeListener(() => {
|
|
483
|
+
if (capRefreshTimer !== void 0) return;
|
|
484
|
+
capRefreshTimer = setTimeout(() => {
|
|
485
|
+
capRefreshTimer = void 0;
|
|
486
|
+
refreshChildCaps("native-cap-change");
|
|
487
|
+
}, 200);
|
|
488
|
+
capRefreshTimer.unref?.();
|
|
489
|
+
});
|
|
490
|
+
const loaded = [];
|
|
491
|
+
const deviceRestorePromises = [];
|
|
492
|
+
for (const { spec, declaration, AddonClass } of loadedModules) try {
|
|
493
|
+
const addon = new AddonClass();
|
|
494
|
+
const context = await createUdsAddonContext(udsChildClient, nodeId, declaration, dataDir, {
|
|
495
|
+
...storageProvider ? { storageProvider } : {},
|
|
496
|
+
dataPlaneSink: { set: (addonId, endpoints) => {
|
|
497
|
+
addonDataPlanes.set(addonId, endpoints);
|
|
498
|
+
} }
|
|
499
|
+
});
|
|
500
|
+
await installManifestPythonDeps(declaration, spec.addonDir, context.deps, context.logger);
|
|
501
|
+
const registeredProviders = /* @__PURE__ */ new Map();
|
|
502
|
+
const initResult = normalizeAddonInitResult(await addon.initialize(context));
|
|
503
|
+
for (const reg of initResult?.providers ?? []) {
|
|
504
|
+
const capName = reg.capability.name;
|
|
505
|
+
registeredProviders.set(capName, reg.provider);
|
|
506
|
+
context.registerProvider(capName, reg.provider);
|
|
507
|
+
const prov = reg.provider;
|
|
508
|
+
if (prov !== null && typeof prov === "object" && !Array.isArray(prov)) {
|
|
509
|
+
allSingletonProviders.set(capName, prov);
|
|
510
|
+
if (capName === "addon-routes") addonRouteProviders.set(spec.addonId, prov);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (initResult?.customActions && initResult.actionHandlers) {
|
|
514
|
+
const handlers = initResult.actionHandlers;
|
|
515
|
+
customActionRegistry.registerAddon(spec.addonId, initResult.customActions, (action, input) => {
|
|
516
|
+
const fn = handlers[action];
|
|
517
|
+
if (!fn) throw new Error(`addon '${spec.addonId}' has no handler for custom action '${action}'`);
|
|
518
|
+
return fn(input);
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
validateProviderRegistrations(spec.addonId, declaration, registeredProviders, context.logger);
|
|
522
|
+
const capNames = [...registeredProviders.keys()];
|
|
523
|
+
context.logger.info(`"${spec.addonId}" caps: ${capNames.length > 0 ? capNames.join(", ") : "(none)"}`);
|
|
524
|
+
runnerManifest.push({
|
|
525
|
+
addonId: spec.addonId,
|
|
526
|
+
capabilities: capNames
|
|
527
|
+
});
|
|
528
|
+
if (typeof addon.restoreDevices === "function") deviceRestorePromises.push(runWorkerDeviceRestoreWithRetry(addon, context, spec.addonId, nodeId));
|
|
529
|
+
addon.postBrokerStart();
|
|
530
|
+
loadedAddonInstances.set(spec.addonId, addon);
|
|
531
|
+
loaded.push({
|
|
532
|
+
addonId: spec.addonId,
|
|
533
|
+
declaration,
|
|
534
|
+
addon,
|
|
535
|
+
initResult: initResult ?? null
|
|
536
|
+
});
|
|
537
|
+
} catch (err) {
|
|
538
|
+
console.error(`[addon-runner] Init failed for "${spec.addonId}": ${errMsg(err)}`);
|
|
539
|
+
process.exit(1);
|
|
540
|
+
}
|
|
541
|
+
if (deviceRestorePromises.length > 0 && !shuttingDown) await Promise.allSettled(deviceRestorePromises);
|
|
542
|
+
refreshChildCaps("post-init");
|
|
543
|
+
let hubReachableFired = false;
|
|
544
|
+
const fireHubReachableOnce = () => {
|
|
545
|
+
if (hubReachableFired) return;
|
|
546
|
+
hubReachableFired = true;
|
|
547
|
+
for (const entry of loaded) if (typeof entry.addon.onHubReachable === "function") Promise.resolve(entry.addon.onHubReachable()).catch((err) => {
|
|
548
|
+
runnerLog.error(`"${entry.addonId}" onHubReachable() threw`, { meta: { error: errMsg(err) } });
|
|
549
|
+
});
|
|
550
|
+
};
|
|
551
|
+
udsChildClient.onConnected(() => {
|
|
552
|
+
if (hubReachableFired) refreshChildCaps("on reconnect");
|
|
553
|
+
fireHubReachableOnce();
|
|
554
|
+
});
|
|
555
|
+
fireHubReachableOnce();
|
|
556
|
+
runnerLog.info(`"${nodeId}" started — ${loaded.length} addon(s) ready (UDS connected)`);
|
|
557
|
+
process.stdout.write(`READY:${nodeId}\n`);
|
|
558
|
+
const shutdown = async () => {
|
|
559
|
+
shuttingDown = true;
|
|
560
|
+
setWorkerNativeCapsChangeListener(null);
|
|
561
|
+
if (capRefreshTimer !== void 0) {
|
|
562
|
+
clearTimeout(capRefreshTimer);
|
|
563
|
+
capRefreshTimer = void 0;
|
|
564
|
+
}
|
|
565
|
+
runnerLog.info(`"${nodeId}" shutting down ${loaded.length} addon(s)...`);
|
|
566
|
+
for (const entry of loaded) try {
|
|
567
|
+
await entry.addon.shutdown();
|
|
568
|
+
} catch (err) {
|
|
569
|
+
runnerLog.error(`"${entry.addonId}" shutdown threw`, { meta: { error: errMsg(err) } });
|
|
570
|
+
}
|
|
571
|
+
try {
|
|
572
|
+
await udsChildClient.close();
|
|
573
|
+
} catch (err) {
|
|
574
|
+
runnerLog.warn("UDS child transport close threw during shutdown", { meta: { error: errMsg(err) } });
|
|
575
|
+
}
|
|
576
|
+
process.exit(0);
|
|
577
|
+
};
|
|
578
|
+
process.on("SIGTERM", () => {
|
|
579
|
+
shutdown();
|
|
580
|
+
});
|
|
581
|
+
process.on("SIGINT", () => {
|
|
582
|
+
shutdown();
|
|
583
|
+
});
|
|
584
|
+
const initialPpid = process.ppid;
|
|
585
|
+
setInterval(() => {
|
|
586
|
+
if (process.ppid === 1 && initialPpid !== 1) {
|
|
587
|
+
console.error(`[addon-runner] "${nodeId}" parent died (reparented to init) — exiting`);
|
|
588
|
+
shutdown();
|
|
589
|
+
}
|
|
590
|
+
}, 5e3);
|
|
591
|
+
}
|
|
592
|
+
main().catch((err) => {
|
|
593
|
+
console.error(`[addon-runner] Fatal error: ${errMsg(err)}`);
|
|
594
|
+
process.exit(1);
|
|
595
|
+
});
|
|
596
|
+
//#endregion
|
|
597
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AuthManager } from './auth-manager.js';
|
|
2
|
+
import { ApiKeyRecord, SettingsStoreClient } from '@camstack/types';
|
|
3
|
+
export type { ApiKeyRecord };
|
|
4
|
+
interface CreateApiKeyInput {
|
|
5
|
+
label: string;
|
|
6
|
+
isAdmin?: boolean;
|
|
7
|
+
allowedProviders?: string[] | '*';
|
|
8
|
+
allowedDevices?: Record<string, string[] | '*'>;
|
|
9
|
+
}
|
|
10
|
+
export interface ApiKeyStorageAccess {
|
|
11
|
+
getStore(): SettingsStoreClient;
|
|
12
|
+
}
|
|
13
|
+
export declare class ApiKeyManager {
|
|
14
|
+
private readonly storageAccess;
|
|
15
|
+
private readonly auth;
|
|
16
|
+
constructor(storageAccess: ApiKeyStorageAccess, auth: AuthManager);
|
|
17
|
+
private get store();
|
|
18
|
+
create(input: CreateApiKeyInput): Promise<{
|
|
19
|
+
record: ApiKeyRecord;
|
|
20
|
+
token: string;
|
|
21
|
+
}>;
|
|
22
|
+
validateToken(token: string): Promise<ApiKeyRecord | null>;
|
|
23
|
+
listAll(): Promise<Omit<ApiKeyRecord, 'tokenHash'>[]>;
|
|
24
|
+
revoke(id: string): Promise<void>;
|
|
25
|
+
findById(id: string): Promise<ApiKeyRecord | null>;
|
|
26
|
+
}
|