@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,29 @@
|
|
|
1
|
+
import { PlatformScore, HardwareInfo, ModelRequirement, ResolvedInferenceConfig } from '@camstack/types';
|
|
2
|
+
export declare class InferenceConfigResolver {
|
|
3
|
+
private readonly scores;
|
|
4
|
+
private readonly hardware;
|
|
5
|
+
constructor(scores: readonly PlatformScore[], hardware: HardwareInfo);
|
|
6
|
+
/**
|
|
7
|
+
* Compute accuracy/backend weights based on available system RAM.
|
|
8
|
+
* availableRAM_MB is now sourced from systeminformation (reliable cross-platform),
|
|
9
|
+
* not os.freemem() which is broken on macOS.
|
|
10
|
+
*
|
|
11
|
+
* - > 16 GB available: prefer larger, more accurate models (accuracy 0.6, backend 0.4)
|
|
12
|
+
* - > 8 GB available: balanced (accuracy 0.5, backend 0.5)
|
|
13
|
+
* - <= 8 GB available: prefer speed (accuracy 0.4, backend 0.6)
|
|
14
|
+
*/
|
|
15
|
+
private getWeights;
|
|
16
|
+
/**
|
|
17
|
+
* Given an addon's model requirements, pick the best model + runtime + backend.
|
|
18
|
+
*
|
|
19
|
+
* Algorithm:
|
|
20
|
+
* 1. Filter models by available RAM (minRAM_MB < 25% of available RAM)
|
|
21
|
+
* 2. For each remaining model, find the best platform score whose format
|
|
22
|
+
* is available in the model's formats
|
|
23
|
+
* 3. Pick the model with the highest combined score using RAM-adaptive weights:
|
|
24
|
+
* - High RAM (>16 GB): accuracy × 0.6 + backend × 0.4 (prefer accuracy)
|
|
25
|
+
* - Mid RAM (>8 GB): accuracy × 0.5 + backend × 0.5 (balanced)
|
|
26
|
+
* - Low RAM (<=8 GB): accuracy × 0.4 + backend × 0.6 (prefer speed)
|
|
27
|
+
*/
|
|
28
|
+
resolve(requirements: readonly ModelRequirement[]): ResolvedInferenceConfig;
|
|
29
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GpuInfo, NpuInfo } from '@camstack/types';
|
|
2
|
+
export interface AcceleratorFs {
|
|
3
|
+
listRenderNodes(): readonly string[];
|
|
4
|
+
readVendor(renderNode: string): string;
|
|
5
|
+
exists(path: string): boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface IntelAccelerators {
|
|
8
|
+
gpu: GpuInfo | null;
|
|
9
|
+
npu: NpuInfo | null;
|
|
10
|
+
}
|
|
11
|
+
export declare function classifyIntelAccelerators(fsx: AcceleratorFs): IntelAccelerators;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { HardwareInfo, PlatformCapabilities, IScopedLogger } from '@camstack/types';
|
|
2
|
+
export declare class PlatformScorer {
|
|
3
|
+
private cached;
|
|
4
|
+
private readonly logger;
|
|
5
|
+
/**
|
|
6
|
+
* Path to the embedded portable Python (from `ctx.deps.ensurePython()`).
|
|
7
|
+
* The Docker hub ships NO system `python3` — inference runs on the
|
|
8
|
+
* downloaded portable build — so probing system `python3`/`python` would
|
|
9
|
+
* wrongly report "Python not found" and never surface coreml/openvino
|
|
10
|
+
* backends. When set, the Python module probes run against THIS binary;
|
|
11
|
+
* `null` falls back to a system `python3`/`python` lookup (dev / agent
|
|
12
|
+
* environments where Python is on PATH).
|
|
13
|
+
*/
|
|
14
|
+
private readonly embeddedPythonPath;
|
|
15
|
+
constructor(logger?: IScopedLogger, embeddedPythonPath?: string | null);
|
|
16
|
+
/**
|
|
17
|
+
* Probe hardware + runtimes and score all backend combos.
|
|
18
|
+
*
|
|
19
|
+
* An optional `onPhase` callback is invoked at each step so consumers
|
|
20
|
+
* (e.g. the platform-probe addon) can emit live progress events on
|
|
21
|
+
* the event bus. The callback takes a phase id + a typed payload; all
|
|
22
|
+
* phases fire in strict order: `started` → `hardware` → `node-backends`
|
|
23
|
+
* → `python-backends` → `scored` → `done`. On failure, `error` fires
|
|
24
|
+
* once with the exception. Cached after first call.
|
|
25
|
+
*/
|
|
26
|
+
probe(onPhase?: (phase: string, payload?: Record<string, unknown>) => void): Promise<PlatformCapabilities>;
|
|
27
|
+
probeHardware(): Promise<HardwareInfo>;
|
|
28
|
+
private probePythonBackends;
|
|
29
|
+
private scoreBackends;
|
|
30
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure reconciliation core for the remote-access orchestrator's durable
|
|
3
|
+
* `enabledProviders` set (D8 delivery-rule fix).
|
|
4
|
+
*
|
|
5
|
+
* Why this module exists
|
|
6
|
+
* ----------------------
|
|
7
|
+
* `enabledProviders: string[]` is the operator's "auto-start on boot"
|
|
8
|
+
* intent — a DURABLE setting. Historically it was written ONLY from two
|
|
9
|
+
* fire-and-forget bus events (`NetworkTunnelStarted` / `Stopped`). A
|
|
10
|
+
* dropped event (hub restart, addon crash-respawn, hub↔orchestrator
|
|
11
|
+
* partition) silently diverges the persisted set from reality:
|
|
12
|
+
* - dropped `Started` → tunnel running, not recorded → no auto-start
|
|
13
|
+
* - dropped `Stopped` → tunnel stopped, still recorded → boot restarts
|
|
14
|
+
* an operator-stopped tunnel.
|
|
15
|
+
*
|
|
16
|
+
* A durable setting must not be written by a lossy event. The fix is an
|
|
17
|
+
* idempotent reconcile pass that pulls authoritative state via an
|
|
18
|
+
* ACKNOWLEDGED RPC (`network-access` cap `getStatus`) on every provider
|
|
19
|
+
* (re)connect — mirroring the kernel readiness-registry hydration via
|
|
20
|
+
* `$readiness.getSnapshot`. The lifecycle events stay (still fine for
|
|
21
|
+
* live UI dashboards); only the PERSISTENCE WRITE moves to the RPC pull.
|
|
22
|
+
*
|
|
23
|
+
* Reconcile semantics — ADD-ONLY (deliberately conservative)
|
|
24
|
+
* ----------------------------------------------------------
|
|
25
|
+
* `connected: true` from an acked RPC is UNAMBIGUOUS: the tunnel is up,
|
|
26
|
+
* so the operator must have started it → safe to ADD to `enabledProviders`
|
|
27
|
+
* (recovers a dropped `NetworkTunnelStarted` event).
|
|
28
|
+
*
|
|
29
|
+
* `connected: false` is AMBIGUOUS:
|
|
30
|
+
* - Provider registered but not yet started (normal cold-boot state,
|
|
31
|
+
* BEFORE `autoStartEnabledProviders` has called `start()`).
|
|
32
|
+
* - Provider transiently down (network blip, restart in progress).
|
|
33
|
+
* - Provider intentionally stopped by the operator.
|
|
34
|
+
* `getStatus()` reports connectivity, NOT operator intent. Acting on
|
|
35
|
+
* `connected: false` would wipe `enabledProviders` on every hub restart
|
|
36
|
+
* (providers re-register ~15-20 s before `start()` is called), defeating
|
|
37
|
+
* the purpose of the durable enabled-set. Therefore the reconcile pass
|
|
38
|
+
* NEVER removes on `connected: false`.
|
|
39
|
+
*
|
|
40
|
+
* Removal of operator intent stays event-driven (`NetworkTunnelStopped`
|
|
41
|
+
* → `markEnabled(id, false)`). That event is emitted synchronously by
|
|
42
|
+
* the provider's own `stop()` call — it is the reliable signal that the
|
|
43
|
+
* operator actually stopped the tunnel. A failed/absent RPC probe also
|
|
44
|
+
* leaves the provider untouched.
|
|
45
|
+
*
|
|
46
|
+
* // Follow-up: covering the dropped-`Stopped` direction (a `Stopped`
|
|
47
|
+
* // event lost → tunnel stopped but still in enabledProviders → boot
|
|
48
|
+
* // wrongly restarts it) would require a provider-side persisted
|
|
49
|
+
* // operator-intent flag that `getStatus()` surfaces. That is a larger
|
|
50
|
+
* // multi-addon change (the audit's "approach 2") and is OUT OF SCOPE
|
|
51
|
+
* // for this backstop. The current fix covers the higher-impact direction
|
|
52
|
+
* // (dropped `Started` → operator's running tunnel not auto-restarted).
|
|
53
|
+
*
|
|
54
|
+
* The function is pure: the addon owns the RPC fan-out + the persist
|
|
55
|
+
* call, so this core is unit-testable without a CapabilityRegistry.
|
|
56
|
+
*/
|
|
57
|
+
/**
|
|
58
|
+
* Outcome of one provider's `getStatus()` RPC probe.
|
|
59
|
+
*
|
|
60
|
+
* `ok: true` → the RPC was acknowledged; `connected` is authoritative.
|
|
61
|
+
* `ok: false` → the RPC threw / timed out; state is unknown and the
|
|
62
|
+
* provider's membership must be left as-is.
|
|
63
|
+
*/
|
|
64
|
+
export type ProviderProbeResult = {
|
|
65
|
+
readonly addonId: string;
|
|
66
|
+
readonly ok: true;
|
|
67
|
+
readonly connected: boolean;
|
|
68
|
+
} | {
|
|
69
|
+
readonly addonId: string;
|
|
70
|
+
readonly ok: false;
|
|
71
|
+
};
|
|
72
|
+
export interface ReconcileResult {
|
|
73
|
+
/** The corrected enabled-set, sorted for stable persistence. */
|
|
74
|
+
readonly nextEnabled: readonly string[];
|
|
75
|
+
/** `true` when `nextEnabled` differs from the input — caller persists only then. */
|
|
76
|
+
readonly changed: boolean;
|
|
77
|
+
/** addonIds added because an acked RPC reported them connected. */
|
|
78
|
+
readonly added: readonly string[];
|
|
79
|
+
/**
|
|
80
|
+
* Always empty — reconcile is add-only. Removal of operator intent is
|
|
81
|
+
* event-driven (`NetworkTunnelStopped`). Kept in the type so callers
|
|
82
|
+
* that log `result.removed` continue to compile without changes.
|
|
83
|
+
*/
|
|
84
|
+
readonly removed: readonly string[];
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Reconcile the durable `enabledProviders` set against authoritative
|
|
88
|
+
* RPC probe results. Pure — no I/O. ADD-ONLY: only adds providers whose
|
|
89
|
+
* acked `getStatus()` reports `connected: true`. Never removes on
|
|
90
|
+
* `connected: false` — see module doc for the full rationale.
|
|
91
|
+
*
|
|
92
|
+
* @param currentEnabled - the persisted enabled-set before reconcile.
|
|
93
|
+
* @param probes - one entry per provider whose `getStatus()` RPC was
|
|
94
|
+
* attempted this pass. Providers absent from this list are untouched.
|
|
95
|
+
*/
|
|
96
|
+
export declare function reconcileEnabledProviders(currentEnabled: readonly string[], probes: readonly ProviderProbeResult[]): ReconcileResult;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { RemoteAccessOrchestratorAddon, default } from './remote-access-orchestrator.addon.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
require("../../chunk-Cek0wNdY.js");
|
|
6
|
+
const require_builtins_remote_access_orchestrator_remote_access_orchestrator_addon = require("./remote-access-orchestrator.addon.js");
|
|
7
|
+
exports.RemoteAccessOrchestratorAddon = require_builtins_remote_access_orchestrator_remote_access_orchestrator_addon.RemoteAccessOrchestratorAddon;
|
|
8
|
+
exports.default = require_builtins_remote_access_orchestrator_remote_access_orchestrator_addon.RemoteAccessOrchestratorAddon;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { BaseAddon, ProviderRegistration } from '@camstack/types';
|
|
2
|
+
interface RemoteAccessOrchestratorConfig {
|
|
3
|
+
/**
|
|
4
|
+
* addonIds the operator has explicitly Started. Auto-respawned on
|
|
5
|
+
* boot so a tunnel set up once stays up across hub restarts.
|
|
6
|
+
*/
|
|
7
|
+
readonly enabledProviders: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
export declare class RemoteAccessOrchestratorAddon extends BaseAddon<RemoteAccessOrchestratorConfig> {
|
|
10
|
+
constructor();
|
|
11
|
+
protected onInitialize(): Promise<ProviderRegistration[]>;
|
|
12
|
+
/**
|
|
13
|
+
* Maintain the persisted `enabledProviders` set from a tunnel
|
|
14
|
+
* lifecycle event. `source.id` is `string | number`; `network-access`
|
|
15
|
+
* providers emit with `type: 'addon'` so it is always the addonId
|
|
16
|
+
* string. Non-string / non-addon sources are ignored defensively.
|
|
17
|
+
*/
|
|
18
|
+
private onTunnelLifecycle;
|
|
19
|
+
/**
|
|
20
|
+
* Probe every locally-visible `network-access` provider's
|
|
21
|
+
* `getStatus()` over RPC and reconcile the durable `enabledProviders`
|
|
22
|
+
* set against the result. This is the D8 backstop for dropped
|
|
23
|
+
* `NetworkTunnelStarted` events: if `connected: true` is acked, the
|
|
24
|
+
* provider is added to `enabledProviders`. ADD-ONLY — never removes on
|
|
25
|
+
* `connected: false` (see enabled-providers-reconcile.ts for rationale).
|
|
26
|
+
*
|
|
27
|
+
* `getStatus()` is an acknowledged cap RPC — a transport blip surfaces
|
|
28
|
+
* as a rejected promise (recorded as `ok: false`, membership left
|
|
29
|
+
* untouched), never as a silent lossy write. Runs on every provider
|
|
30
|
+
* (re)connect via the `watchCapability` hooks, mirroring the kernel
|
|
31
|
+
* readiness-registry hydration on `$node.connected`. Safe to call
|
|
32
|
+
* before `autoStartEnabledProviders`: add-only semantics mean it cannot
|
|
33
|
+
* wipe the enabled-set even when providers are registered-not-yet-started.
|
|
34
|
+
*/
|
|
35
|
+
private reconcileAndPersist;
|
|
36
|
+
private autoStartEnabledProviders;
|
|
37
|
+
private markEnabled;
|
|
38
|
+
private resolveImpl;
|
|
39
|
+
}
|
|
40
|
+
export default RemoteAccessOrchestratorAddon;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
require("../../chunk-Cek0wNdY.js");
|
|
6
|
+
let _camstack_types = require("@camstack/types");
|
|
7
|
+
//#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
|
|
8
|
+
/**
|
|
9
|
+
* Reconcile the durable `enabledProviders` set against authoritative
|
|
10
|
+
* RPC probe results. Pure — no I/O. ADD-ONLY: only adds providers whose
|
|
11
|
+
* acked `getStatus()` reports `connected: true`. Never removes on
|
|
12
|
+
* `connected: false` — see module doc for the full rationale.
|
|
13
|
+
*
|
|
14
|
+
* @param currentEnabled - the persisted enabled-set before reconcile.
|
|
15
|
+
* @param probes - one entry per provider whose `getStatus()` RPC was
|
|
16
|
+
* attempted this pass. Providers absent from this list are untouched.
|
|
17
|
+
*/
|
|
18
|
+
function reconcileEnabledProviders(currentEnabled, probes) {
|
|
19
|
+
const next = new Set(currentEnabled);
|
|
20
|
+
const added = [];
|
|
21
|
+
for (const probe of probes) {
|
|
22
|
+
if (!probe.ok) continue;
|
|
23
|
+
if (probe.connected) {
|
|
24
|
+
if (!next.has(probe.addonId)) {
|
|
25
|
+
next.add(probe.addonId);
|
|
26
|
+
added.push(probe.addonId);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
nextEnabled: [...next].toSorted(),
|
|
32
|
+
changed: added.length > 0,
|
|
33
|
+
added,
|
|
34
|
+
removed: []
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.ts
|
|
39
|
+
/**
|
|
40
|
+
* Remote-access orchestrator — backend-only boot-autostart service for
|
|
41
|
+
* the `network-access` collection (Cloudflare Tunnel, ngrok, Tailscale, …).
|
|
42
|
+
*
|
|
43
|
+
* Retired its `remote-access` facade cap (2026-05-15): the admin UI now
|
|
44
|
+
* talks to the `network-access` collection cap directly via generic
|
|
45
|
+
* per-`addonId` routing, so this addon registers NO capability.
|
|
46
|
+
*
|
|
47
|
+
* What it still owns — the load-bearing logic:
|
|
48
|
+
* The orchestrator owns the "operator wants this provider running"
|
|
49
|
+
* intent — an `enabledProviders: string[]` slice in its addon-store
|
|
50
|
+
* blob (BaseAddon.config). On boot we iterate the list and call
|
|
51
|
+
* `provider.start()` for each enabled entry, so a tunnel set up once
|
|
52
|
+
* stays up across hub restarts.
|
|
53
|
+
*
|
|
54
|
+
* Since start/stop no longer flow through this addon, the enabled-set
|
|
55
|
+
* is kept in sync from two sources:
|
|
56
|
+
* 1. `NetworkTunnelStarted` / `NetworkTunnelStopped` bus events —
|
|
57
|
+
* fast, but lossy (fire-and-forget broadcasts). They drive both
|
|
58
|
+
* the live UI and removal from the durable set (`Stopped` is the
|
|
59
|
+
* only reliable "operator stopped it" signal).
|
|
60
|
+
* 2. An RPC-driven ADD-ONLY RECONCILE pass (`reconcileEnabledProviders`)
|
|
61
|
+
* that pulls authoritative `connected` state via the `network-access`
|
|
62
|
+
* cap's `getStatus()` on every provider (re)connect. THIS covers
|
|
63
|
+
* dropped `NetworkTunnelStarted` events — if `connected: true` is
|
|
64
|
+
* acked, the provider is added to `enabledProviders`. It NEVER
|
|
65
|
+
* removes on `connected: false` because that signal is ambiguous
|
|
66
|
+
* (registered-not-yet-started vs transiently-down vs intentionally
|
|
67
|
+
* stopped). Running before `autoStartEnabledProviders` is safe:
|
|
68
|
+
* it can only add already-connected providers, never evict.
|
|
69
|
+
*/
|
|
70
|
+
var RemoteAccessOrchestratorAddon = class extends _camstack_types.BaseAddon {
|
|
71
|
+
constructor() {
|
|
72
|
+
super({ enabledProviders: [] });
|
|
73
|
+
}
|
|
74
|
+
async onInitialize() {
|
|
75
|
+
this.ctx.logger.info("Remote-access orchestrator initialized (backend-only)", { meta: { enabledCount: this.config.enabledProviders.length } });
|
|
76
|
+
this.ctx.eventBus?.subscribe({ category: _camstack_types.EventCategory.NetworkTunnelStarted }, (event) => {
|
|
77
|
+
this.onTunnelLifecycle(event.source, true);
|
|
78
|
+
});
|
|
79
|
+
this.ctx.eventBus?.subscribe({ category: _camstack_types.EventCategory.NetworkTunnelStopped }, (event) => {
|
|
80
|
+
this.onTunnelLifecycle(event.source, false);
|
|
81
|
+
});
|
|
82
|
+
setImmediate(() => {
|
|
83
|
+
this.autoStartEnabledProviders();
|
|
84
|
+
});
|
|
85
|
+
this.watchCapability("network-access", { onReady: () => {
|
|
86
|
+
this.autoStartEnabledProviders();
|
|
87
|
+
} });
|
|
88
|
+
this.watchCapability("mesh-network", { onReady: () => {
|
|
89
|
+
this.autoStartEnabledProviders();
|
|
90
|
+
} });
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Maintain the persisted `enabledProviders` set from a tunnel
|
|
95
|
+
* lifecycle event. `source.id` is `string | number`; `network-access`
|
|
96
|
+
* providers emit with `type: 'addon'` so it is always the addonId
|
|
97
|
+
* string. Non-string / non-addon sources are ignored defensively.
|
|
98
|
+
*/
|
|
99
|
+
async onTunnelLifecycle(source, started) {
|
|
100
|
+
if (source.type !== "addon" || typeof source.id !== "string") {
|
|
101
|
+
this.ctx.logger.warn("tunnel lifecycle event with non-addon source — ignoring", { meta: {
|
|
102
|
+
sourceType: source.type,
|
|
103
|
+
sourceId: source.id,
|
|
104
|
+
started
|
|
105
|
+
} });
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
await this.markEnabled(source.id, started);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Probe every locally-visible `network-access` provider's
|
|
112
|
+
* `getStatus()` over RPC and reconcile the durable `enabledProviders`
|
|
113
|
+
* set against the result. This is the D8 backstop for dropped
|
|
114
|
+
* `NetworkTunnelStarted` events: if `connected: true` is acked, the
|
|
115
|
+
* provider is added to `enabledProviders`. ADD-ONLY — never removes on
|
|
116
|
+
* `connected: false` (see enabled-providers-reconcile.ts for rationale).
|
|
117
|
+
*
|
|
118
|
+
* `getStatus()` is an acknowledged cap RPC — a transport blip surfaces
|
|
119
|
+
* as a rejected promise (recorded as `ok: false`, membership left
|
|
120
|
+
* untouched), never as a silent lossy write. Runs on every provider
|
|
121
|
+
* (re)connect via the `watchCapability` hooks, mirroring the kernel
|
|
122
|
+
* readiness-registry hydration on `$node.connected`. Safe to call
|
|
123
|
+
* before `autoStartEnabledProviders`: add-only semantics mean it cannot
|
|
124
|
+
* wipe the enabled-set even when providers are registered-not-yet-started.
|
|
125
|
+
*/
|
|
126
|
+
async reconcileAndPersist() {
|
|
127
|
+
const entries = this.capabilities?.getCollectionEntries("network-access") ?? [];
|
|
128
|
+
if (entries.length === 0) return;
|
|
129
|
+
const probes = await Promise.all(entries.map(async ([addonId, impl]) => {
|
|
130
|
+
if (!impl.getStatus) return {
|
|
131
|
+
addonId,
|
|
132
|
+
ok: false
|
|
133
|
+
};
|
|
134
|
+
try {
|
|
135
|
+
return {
|
|
136
|
+
addonId,
|
|
137
|
+
ok: true,
|
|
138
|
+
connected: (await impl.getStatus()).connected
|
|
139
|
+
};
|
|
140
|
+
} catch (err) {
|
|
141
|
+
this.ctx.logger.warn("reconcile: getStatus RPC failed — leaving membership as-is", { meta: {
|
|
142
|
+
addonId,
|
|
143
|
+
error: err instanceof Error ? err.message : String(err)
|
|
144
|
+
} });
|
|
145
|
+
return {
|
|
146
|
+
addonId,
|
|
147
|
+
ok: false
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}));
|
|
151
|
+
const result = reconcileEnabledProviders(this.config.enabledProviders, probes);
|
|
152
|
+
if (!result.changed) return;
|
|
153
|
+
this.ctx.logger.info("reconcile: corrected enabledProviders from RPC-pulled state", { meta: {
|
|
154
|
+
added: result.added,
|
|
155
|
+
removed: result.removed
|
|
156
|
+
} });
|
|
157
|
+
await this.updateGlobalSettings({ enabledProviders: result.nextEnabled });
|
|
158
|
+
}
|
|
159
|
+
async autoStartEnabledProviders() {
|
|
160
|
+
await this.reconcileAndPersist();
|
|
161
|
+
const ids = this.config.enabledProviders;
|
|
162
|
+
if (ids.length === 0) return;
|
|
163
|
+
this.ctx.logger.info("Auto-starting enabled remote-access providers", { meta: { addonIds: [...ids] } });
|
|
164
|
+
for (const addonId of ids) try {
|
|
165
|
+
const impl = this.resolveImpl(addonId);
|
|
166
|
+
if (!impl?.start) {
|
|
167
|
+
this.ctx.logger.warn("autostart: provider not ready or unsupported", { meta: {
|
|
168
|
+
addonId,
|
|
169
|
+
hasImpl: !!impl,
|
|
170
|
+
hasStart: !!impl?.start
|
|
171
|
+
} });
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (impl.getStatus) {
|
|
175
|
+
const status = await impl.getStatus().catch(() => null);
|
|
176
|
+
if (status?.connected) {
|
|
177
|
+
this.ctx.logger.info("autostart: provider already connected — skipping", { meta: {
|
|
178
|
+
addonId,
|
|
179
|
+
url: status.endpoint?.url
|
|
180
|
+
} });
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const endpoint = await impl.start();
|
|
185
|
+
this.ctx.logger.info("autostart: provider started", { meta: {
|
|
186
|
+
addonId,
|
|
187
|
+
url: endpoint.url
|
|
188
|
+
} });
|
|
189
|
+
} catch (err) {
|
|
190
|
+
this.ctx.logger.error("autostart: provider start failed", { meta: {
|
|
191
|
+
addonId,
|
|
192
|
+
error: err instanceof Error ? err.message : String(err)
|
|
193
|
+
} });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async markEnabled(addonId, enabled) {
|
|
197
|
+
const current = new Set(this.config.enabledProviders);
|
|
198
|
+
const wasEnabled = current.has(addonId);
|
|
199
|
+
if (enabled) current.add(addonId);
|
|
200
|
+
else current.delete(addonId);
|
|
201
|
+
if (wasEnabled === enabled) return;
|
|
202
|
+
this.ctx.logger.info("remote-access intent updated", { meta: {
|
|
203
|
+
addonId,
|
|
204
|
+
enabled
|
|
205
|
+
} });
|
|
206
|
+
await this.updateGlobalSettings({ enabledProviders: [...current] });
|
|
207
|
+
}
|
|
208
|
+
resolveImpl(addonId) {
|
|
209
|
+
return (this.capabilities?.getCollectionEntries("network-access") ?? []).find(([id]) => id === addonId)?.[1] ?? null;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
//#endregion
|
|
213
|
+
exports.RemoteAccessOrchestratorAddon = RemoteAccessOrchestratorAddon;
|
|
214
|
+
exports.default = RemoteAccessOrchestratorAddon;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { BaseAddon, EventCategory } from "@camstack/types";
|
|
2
|
+
//#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
|
|
3
|
+
/**
|
|
4
|
+
* Reconcile the durable `enabledProviders` set against authoritative
|
|
5
|
+
* RPC probe results. Pure — no I/O. ADD-ONLY: only adds providers whose
|
|
6
|
+
* acked `getStatus()` reports `connected: true`. Never removes on
|
|
7
|
+
* `connected: false` — see module doc for the full rationale.
|
|
8
|
+
*
|
|
9
|
+
* @param currentEnabled - the persisted enabled-set before reconcile.
|
|
10
|
+
* @param probes - one entry per provider whose `getStatus()` RPC was
|
|
11
|
+
* attempted this pass. Providers absent from this list are untouched.
|
|
12
|
+
*/
|
|
13
|
+
function reconcileEnabledProviders(currentEnabled, probes) {
|
|
14
|
+
const next = new Set(currentEnabled);
|
|
15
|
+
const added = [];
|
|
16
|
+
for (const probe of probes) {
|
|
17
|
+
if (!probe.ok) continue;
|
|
18
|
+
if (probe.connected) {
|
|
19
|
+
if (!next.has(probe.addonId)) {
|
|
20
|
+
next.add(probe.addonId);
|
|
21
|
+
added.push(probe.addonId);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
nextEnabled: [...next].toSorted(),
|
|
27
|
+
changed: added.length > 0,
|
|
28
|
+
added,
|
|
29
|
+
removed: []
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.ts
|
|
34
|
+
/**
|
|
35
|
+
* Remote-access orchestrator — backend-only boot-autostart service for
|
|
36
|
+
* the `network-access` collection (Cloudflare Tunnel, ngrok, Tailscale, …).
|
|
37
|
+
*
|
|
38
|
+
* Retired its `remote-access` facade cap (2026-05-15): the admin UI now
|
|
39
|
+
* talks to the `network-access` collection cap directly via generic
|
|
40
|
+
* per-`addonId` routing, so this addon registers NO capability.
|
|
41
|
+
*
|
|
42
|
+
* What it still owns — the load-bearing logic:
|
|
43
|
+
* The orchestrator owns the "operator wants this provider running"
|
|
44
|
+
* intent — an `enabledProviders: string[]` slice in its addon-store
|
|
45
|
+
* blob (BaseAddon.config). On boot we iterate the list and call
|
|
46
|
+
* `provider.start()` for each enabled entry, so a tunnel set up once
|
|
47
|
+
* stays up across hub restarts.
|
|
48
|
+
*
|
|
49
|
+
* Since start/stop no longer flow through this addon, the enabled-set
|
|
50
|
+
* is kept in sync from two sources:
|
|
51
|
+
* 1. `NetworkTunnelStarted` / `NetworkTunnelStopped` bus events —
|
|
52
|
+
* fast, but lossy (fire-and-forget broadcasts). They drive both
|
|
53
|
+
* the live UI and removal from the durable set (`Stopped` is the
|
|
54
|
+
* only reliable "operator stopped it" signal).
|
|
55
|
+
* 2. An RPC-driven ADD-ONLY RECONCILE pass (`reconcileEnabledProviders`)
|
|
56
|
+
* that pulls authoritative `connected` state via the `network-access`
|
|
57
|
+
* cap's `getStatus()` on every provider (re)connect. THIS covers
|
|
58
|
+
* dropped `NetworkTunnelStarted` events — if `connected: true` is
|
|
59
|
+
* acked, the provider is added to `enabledProviders`. It NEVER
|
|
60
|
+
* removes on `connected: false` because that signal is ambiguous
|
|
61
|
+
* (registered-not-yet-started vs transiently-down vs intentionally
|
|
62
|
+
* stopped). Running before `autoStartEnabledProviders` is safe:
|
|
63
|
+
* it can only add already-connected providers, never evict.
|
|
64
|
+
*/
|
|
65
|
+
var RemoteAccessOrchestratorAddon = class extends BaseAddon {
|
|
66
|
+
constructor() {
|
|
67
|
+
super({ enabledProviders: [] });
|
|
68
|
+
}
|
|
69
|
+
async onInitialize() {
|
|
70
|
+
this.ctx.logger.info("Remote-access orchestrator initialized (backend-only)", { meta: { enabledCount: this.config.enabledProviders.length } });
|
|
71
|
+
this.ctx.eventBus?.subscribe({ category: EventCategory.NetworkTunnelStarted }, (event) => {
|
|
72
|
+
this.onTunnelLifecycle(event.source, true);
|
|
73
|
+
});
|
|
74
|
+
this.ctx.eventBus?.subscribe({ category: EventCategory.NetworkTunnelStopped }, (event) => {
|
|
75
|
+
this.onTunnelLifecycle(event.source, false);
|
|
76
|
+
});
|
|
77
|
+
setImmediate(() => {
|
|
78
|
+
this.autoStartEnabledProviders();
|
|
79
|
+
});
|
|
80
|
+
this.watchCapability("network-access", { onReady: () => {
|
|
81
|
+
this.autoStartEnabledProviders();
|
|
82
|
+
} });
|
|
83
|
+
this.watchCapability("mesh-network", { onReady: () => {
|
|
84
|
+
this.autoStartEnabledProviders();
|
|
85
|
+
} });
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Maintain the persisted `enabledProviders` set from a tunnel
|
|
90
|
+
* lifecycle event. `source.id` is `string | number`; `network-access`
|
|
91
|
+
* providers emit with `type: 'addon'` so it is always the addonId
|
|
92
|
+
* string. Non-string / non-addon sources are ignored defensively.
|
|
93
|
+
*/
|
|
94
|
+
async onTunnelLifecycle(source, started) {
|
|
95
|
+
if (source.type !== "addon" || typeof source.id !== "string") {
|
|
96
|
+
this.ctx.logger.warn("tunnel lifecycle event with non-addon source — ignoring", { meta: {
|
|
97
|
+
sourceType: source.type,
|
|
98
|
+
sourceId: source.id,
|
|
99
|
+
started
|
|
100
|
+
} });
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
await this.markEnabled(source.id, started);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Probe every locally-visible `network-access` provider's
|
|
107
|
+
* `getStatus()` over RPC and reconcile the durable `enabledProviders`
|
|
108
|
+
* set against the result. This is the D8 backstop for dropped
|
|
109
|
+
* `NetworkTunnelStarted` events: if `connected: true` is acked, the
|
|
110
|
+
* provider is added to `enabledProviders`. ADD-ONLY — never removes on
|
|
111
|
+
* `connected: false` (see enabled-providers-reconcile.ts for rationale).
|
|
112
|
+
*
|
|
113
|
+
* `getStatus()` is an acknowledged cap RPC — a transport blip surfaces
|
|
114
|
+
* as a rejected promise (recorded as `ok: false`, membership left
|
|
115
|
+
* untouched), never as a silent lossy write. Runs on every provider
|
|
116
|
+
* (re)connect via the `watchCapability` hooks, mirroring the kernel
|
|
117
|
+
* readiness-registry hydration on `$node.connected`. Safe to call
|
|
118
|
+
* before `autoStartEnabledProviders`: add-only semantics mean it cannot
|
|
119
|
+
* wipe the enabled-set even when providers are registered-not-yet-started.
|
|
120
|
+
*/
|
|
121
|
+
async reconcileAndPersist() {
|
|
122
|
+
const entries = this.capabilities?.getCollectionEntries("network-access") ?? [];
|
|
123
|
+
if (entries.length === 0) return;
|
|
124
|
+
const probes = await Promise.all(entries.map(async ([addonId, impl]) => {
|
|
125
|
+
if (!impl.getStatus) return {
|
|
126
|
+
addonId,
|
|
127
|
+
ok: false
|
|
128
|
+
};
|
|
129
|
+
try {
|
|
130
|
+
return {
|
|
131
|
+
addonId,
|
|
132
|
+
ok: true,
|
|
133
|
+
connected: (await impl.getStatus()).connected
|
|
134
|
+
};
|
|
135
|
+
} catch (err) {
|
|
136
|
+
this.ctx.logger.warn("reconcile: getStatus RPC failed — leaving membership as-is", { meta: {
|
|
137
|
+
addonId,
|
|
138
|
+
error: err instanceof Error ? err.message : String(err)
|
|
139
|
+
} });
|
|
140
|
+
return {
|
|
141
|
+
addonId,
|
|
142
|
+
ok: false
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}));
|
|
146
|
+
const result = reconcileEnabledProviders(this.config.enabledProviders, probes);
|
|
147
|
+
if (!result.changed) return;
|
|
148
|
+
this.ctx.logger.info("reconcile: corrected enabledProviders from RPC-pulled state", { meta: {
|
|
149
|
+
added: result.added,
|
|
150
|
+
removed: result.removed
|
|
151
|
+
} });
|
|
152
|
+
await this.updateGlobalSettings({ enabledProviders: result.nextEnabled });
|
|
153
|
+
}
|
|
154
|
+
async autoStartEnabledProviders() {
|
|
155
|
+
await this.reconcileAndPersist();
|
|
156
|
+
const ids = this.config.enabledProviders;
|
|
157
|
+
if (ids.length === 0) return;
|
|
158
|
+
this.ctx.logger.info("Auto-starting enabled remote-access providers", { meta: { addonIds: [...ids] } });
|
|
159
|
+
for (const addonId of ids) try {
|
|
160
|
+
const impl = this.resolveImpl(addonId);
|
|
161
|
+
if (!impl?.start) {
|
|
162
|
+
this.ctx.logger.warn("autostart: provider not ready or unsupported", { meta: {
|
|
163
|
+
addonId,
|
|
164
|
+
hasImpl: !!impl,
|
|
165
|
+
hasStart: !!impl?.start
|
|
166
|
+
} });
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (impl.getStatus) {
|
|
170
|
+
const status = await impl.getStatus().catch(() => null);
|
|
171
|
+
if (status?.connected) {
|
|
172
|
+
this.ctx.logger.info("autostart: provider already connected — skipping", { meta: {
|
|
173
|
+
addonId,
|
|
174
|
+
url: status.endpoint?.url
|
|
175
|
+
} });
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const endpoint = await impl.start();
|
|
180
|
+
this.ctx.logger.info("autostart: provider started", { meta: {
|
|
181
|
+
addonId,
|
|
182
|
+
url: endpoint.url
|
|
183
|
+
} });
|
|
184
|
+
} catch (err) {
|
|
185
|
+
this.ctx.logger.error("autostart: provider start failed", { meta: {
|
|
186
|
+
addonId,
|
|
187
|
+
error: err instanceof Error ? err.message : String(err)
|
|
188
|
+
} });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async markEnabled(addonId, enabled) {
|
|
192
|
+
const current = new Set(this.config.enabledProviders);
|
|
193
|
+
const wasEnabled = current.has(addonId);
|
|
194
|
+
if (enabled) current.add(addonId);
|
|
195
|
+
else current.delete(addonId);
|
|
196
|
+
if (wasEnabled === enabled) return;
|
|
197
|
+
this.ctx.logger.info("remote-access intent updated", { meta: {
|
|
198
|
+
addonId,
|
|
199
|
+
enabled
|
|
200
|
+
} });
|
|
201
|
+
await this.updateGlobalSettings({ enabledProviders: [...current] });
|
|
202
|
+
}
|
|
203
|
+
resolveImpl(addonId) {
|
|
204
|
+
return (this.capabilities?.getCollectionEntries("network-access") ?? []).find(([id]) => id === addonId)?.[1] ?? null;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
//#endregion
|
|
208
|
+
export { RemoteAccessOrchestratorAddon, RemoteAccessOrchestratorAddon as default };
|