@camstack/server 1.2.593 → 1.2.594
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-root-inventory.js +11 -50
- package/dist/agent/addon-scan.js +6 -44
- package/dist/agent/agent-addon-runtime.js +3 -8
- package/dist/agent/agent-addon-updates.js +12 -52
- package/dist/agent/agent-cap-dispatch-service.js +3 -5
- package/dist/agent/agent-config.js +7 -42
- package/dist/agent/agent-deploy-swap.js +5 -41
- package/dist/agent/agent-group-runner.js +8 -47
- package/dist/agent/agent-http-auth.js +10 -22
- package/dist/agent/agent-http.js +69 -115
- package/dist/agent/agent-hub-addon-update.js +10 -16
- package/dist/agent/agent-hub-registration.js +4 -7
- package/dist/agent/agent-infra-capabilities.js +5 -9
- package/dist/agent/agent-json-file.js +5 -42
- package/dist/agent/agent-mesh-gate.js +5 -8
- package/dist/agent/agent-node-certificates.js +25 -28
- package/dist/agent/agent-pairing-local.js +10 -47
- package/dist/agent/agent-pairing-routes.js +16 -20
- package/dist/agent/agent-pairing-store.js +18 -58
- package/dist/agent/agent-reconnect.js +1 -4
- package/dist/agent/agent-service.js +42 -78
- package/dist/agent/agent-sniffing-listener.js +11 -48
- package/dist/agent/agent-undeploy.js +5 -41
- package/dist/agent/agent-update-routes.js +14 -19
- package/dist/agent/agent-update-service.js +38 -80
- package/dist/agent/apply-model-distribution.js +1 -4
- package/dist/agent/apply-stage-dev-uploads.js +6 -9
- package/dist/agent/derive-hub-url.js +3 -8
- package/dist/agent/fetch-bundle-from-hub.js +6 -9
- package/dist/agent/hubs-found.js +6 -43
- package/dist/agent/infra-boot-guard.js +1 -4
- package/dist/agent/infra-boot-plan.js +2 -6
- package/dist/agent/main.js +158 -186
- package/dist/agent/register-agent-cap-dispatch.js +3 -6
- package/dist/agent/serial-queue.js +2 -7
- package/dist/agent-status-page.js +1 -4
- package/dist/api/addon-upload.js +22 -61
- package/dist/api/agent-addon-delivery.js +6 -11
- package/dist/api/auth-whoami.js +1 -4
- package/dist/api/backup-download-pump.js +5 -9
- package/dist/api/backup-download.js +7 -10
- package/dist/api/core/addon-settings.router.js +16 -19
- package/dist/api/core/agent-pairing.router.js +76 -79
- package/dist/api/core/agents.router.js +21 -24
- package/dist/api/core/auth-admin-tokens.procedures.js +48 -52
- package/dist/api/core/auth-device-cert.procedures.js +62 -67
- package/dist/api/core/auth-limits.js +16 -25
- package/dist/api/core/auth-reauth.procedures.js +32 -37
- package/dist/api/core/auth-revoke-credentials.procedures.js +22 -26
- package/dist/api/core/auth-sessions.procedures.js +52 -56
- package/dist/api/core/auth.router.js +300 -304
- package/dist/api/core/cap-providers.js +74 -121
- package/dist/api/core/capabilities.router.js +34 -37
- package/dist/api/core/capability-preference-writes.js +10 -17
- package/dist/api/core/certificates.router.js +106 -109
- package/dist/api/core/cluster-nodes.router.js +15 -18
- package/dist/api/core/collection-preference.js +2 -6
- package/dist/api/core/event-bus-proxy.router.js +34 -37
- package/dist/api/core/failure-contributions.js +4 -8
- package/dist/api/core/hwaccel.router.js +17 -20
- package/dist/api/core/integration-secrets.js +8 -12
- package/dist/api/core/lifecycle-job-runner.js +13 -49
- package/dist/api/core/live-events.router.js +29 -32
- package/dist/api/core/load-contributions.js +4 -8
- package/dist/api/core/logging-settings.js +30 -43
- package/dist/api/core/logs.router.js +40 -43
- package/dist/api/core/notifications.router.js +15 -18
- package/dist/api/core/public-event-filter.js +4 -8
- package/dist/api/core/repl.router.js +18 -21
- package/dist/api/core/request-census-settings.js +11 -16
- package/dist/api/core/server-management.provider.js +1 -4
- package/dist/api/core/server-name.js +9 -17
- package/dist/api/core/site-location.js +16 -24
- package/dist/api/core/stream-probe.router.js +21 -24
- package/dist/api/core/system-events.router.js +23 -26
- package/dist/api/cors/cors-policy.js +20 -29
- package/dist/api/cors/register-cors-policy.js +9 -14
- package/dist/api/deploy-stage-registry.js +6 -10
- package/dist/api/dev-uploads-bundle.js +12 -50
- package/dist/api/health/adaptive-probe.routes.js +9 -13
- package/dist/api/health/health.routes.js +8 -13
- package/dist/api/hub-cluster-url.js +3 -9
- package/dist/api/model-distributor.js +12 -50
- package/dist/api/oauth2/consent-page.js +10 -13
- package/dist/api/oauth2/oauth2-routes.js +17 -25
- package/dist/api/oauth2/private-host.js +1 -4
- package/dist/api/server-upload-propagate.js +15 -51
- package/dist/api/server-upload.js +13 -50
- package/dist/api/sse-no-compression.js +1 -4
- package/dist/api/static/frame-policy.js +4 -9
- package/dist/api/static/precompressed-asset.js +15 -57
- package/dist/api/static/spa-mount.js +8 -47
- package/dist/api/static/spa-static.js +5 -12
- package/dist/api/tarball-manifest.js +7 -44
- package/dist/api/trpc/cap-mount-helpers.js +7 -12
- package/dist/api/trpc/cap-route-error-formatter.js +10 -13
- package/dist/api/trpc/cap-router-runtime.js +40 -51
- package/dist/api/trpc/client-ip.js +9 -20
- package/dist/api/trpc/core-cap-bridge.js +32 -42
- package/dist/api/trpc/device-collection-dispatch.js +23 -27
- package/dist/api/trpc/device-config-secret-redaction.js +24 -40
- package/dist/api/trpc/generated-cap-mounts.js +218 -222
- package/dist/api/trpc/generated-cap-routers.js +4688 -4850
- package/dist/api/trpc/http-request-census-hook.js +8 -11
- package/dist/api/trpc/http-request-census.js +34 -41
- package/dist/api/trpc/http-request-census.singleton.js +4 -8
- package/dist/api/trpc/localized-schema-paths.js +1 -4
- package/dist/api/trpc/principal-visibility.js +14 -22
- package/dist/api/trpc/remote-addon-update-grant.js +1 -4
- package/dist/api/trpc/scope-access.js +10 -15
- package/dist/api/trpc/server-provided-caps.js +3 -7
- package/dist/api/trpc/share-view-access.js +6 -14
- package/dist/api/trpc/trpc-error-device-tags.js +3 -6
- package/dist/api/trpc/trpc-error-principal.js +2 -6
- package/dist/api/trpc/trpc.context.js +20 -25
- package/dist/api/trpc/trpc.middleware.js +38 -46
- package/dist/api/trpc/trpc.router.js +58 -99
- package/dist/api/trpc/user-error.js +5 -8
- package/dist/api/trpc/ws-request-census.js +17 -24
- package/dist/api/trpc/ws-slow-consumer-guard.js +11 -16
- package/dist/api/upload-auth.js +5 -9
- package/dist/auth/addon-route-jwt-gate.js +4 -7
- package/dist/auth/addon-route-share-gate.js +7 -12
- package/dist/auth/app-confirm-page.js +17 -22
- package/dist/auth/app-confirm-routes.js +26 -31
- package/dist/auth/app-confirmations.js +7 -44
- package/dist/auth/app-page-shell.js +17 -30
- package/dist/auth/app-page-strings.js +11 -17
- package/dist/auth/app-passkey-page.js +16 -21
- package/dist/auth/app-passkey-routes.js +37 -41
- package/dist/auth/app-refused-route.js +13 -18
- package/dist/auth/auth-network-policy.js +11 -15
- package/dist/auth/auth-provider-gate.js +4 -9
- package/dist/auth/auth-rate-limit.js +3 -7
- package/dist/auth/client-address.js +6 -9
- package/dist/auth/device-enroll-proofs.js +6 -43
- package/dist/auth/external-link-intents.js +3 -7
- package/dist/auth/hub-sessions.js +6 -12
- package/dist/auth/native-link-completions.js +7 -11
- package/dist/auth/native-return.js +9 -13
- package/dist/auth/passkey-provider.js +26 -32
- package/dist/auth/second-factors.js +6 -10
- package/dist/auth/session-connections.js +6 -12
- package/dist/auth/session-cookie.js +12 -23
- package/dist/auth/session-lifetimes.js +8 -12
- package/dist/auth/session-mint.js +4 -11
- package/dist/auth/sso-finish-app.js +13 -16
- package/dist/auth/sso-finish-deps.js +21 -27
- package/dist/auth/sso-finish.js +28 -38
- package/dist/auth/sso-routes.js +22 -25
- package/dist/auth/used-bridge-ids.js +1 -5
- package/dist/boot/boot-config.js +16 -55
- package/dist/boot/init-sequence.js +1 -4
- package/dist/boot/integration-id-backfill.js +3 -8
- package/dist/boot/platform-keychain-key.js +5 -42
- package/dist/boot/post-boot.service.js +14 -18
- package/dist/boot/reconcile-lifecycle-jobs.js +3 -6
- package/dist/boot/retired-public-url-env.js +4 -8
- package/dist/bootstrap-packages.js +1 -4
- package/dist/closure-node-modules.js +2 -38
- package/dist/closure-patches/patch-reach.json +6 -2
- package/dist/core/addon/addon-call-gateway.js +1 -5
- package/dist/core/addon/addon-package.service.js +83 -123
- package/dist/core/addon/addon-process-view.js +2 -6
- package/dist/core/addon/addon-realign-plan.js +1 -4
- package/dist/core/addon/addon-registry.service.js +199 -185
- package/dist/core/addon/addon-row-manifest.js +1 -4
- package/dist/core/addon/addon-search.service.js +3 -7
- package/dist/core/addon/addon-settings-provider.js +1 -3
- package/dist/core/addon/addon-uninstall.js +4 -11
- package/dist/core/addon/addon.tokens.js +2 -5
- package/dist/core/addon/device-meta-mirror.js +8 -13
- package/dist/core/addon/forked-custom-actions.js +6 -11
- package/dist/core/addon/integration-visibility.js +3 -6
- package/dist/core/addon/materialize-route-path.js +1 -4
- package/dist/core/addon/package-dir-utils.js +8 -45
- package/dist/core/addon/prune-misplaced-addons.js +5 -43
- package/dist/core/addon/runner-convergence.js +1 -4
- package/dist/core/addon/runner-serialization.js +1 -4
- package/dist/core/addon/runner-spawn-fanout.js +3 -8
- package/dist/core/addon/settings-door-wiring.js +5 -8
- package/dist/core/addon/single-flight-refresh.js +1 -4
- package/dist/core/addon-bridge/addon-bridge.service.js +7 -44
- package/dist/core/addon-pages/addon-pages.service.js +3 -40
- package/dist/core/addon-widgets/addon-widgets.service.js +4 -42
- package/dist/core/agent/agent-addon-backfill.js +9 -17
- package/dist/core/agent/agent-delivery-ledger.js +3 -7
- package/dist/core/agent/agent-liveness-event.js +2 -6
- package/dist/core/agent/agent-pairing-http.js +41 -45
- package/dist/core/agent/agent-registry.service.js +53 -92
- package/dist/core/agent/cluster-node-history-mirror.js +3 -7
- package/dist/core/agent/cluster-node-history-store.js +113 -58
- package/dist/core/agent/hub-agent-pairing.js +1 -5
- package/dist/core/agent/node-certificates-client.js +3 -6
- package/dist/core/agent/node-directory.js +1 -4
- package/dist/core/app-links/well-known-app-links.js +2 -6
- package/dist/core/auth/auth.service.js +3 -8
- package/dist/core/auth/handoff-code.service.js +11 -49
- package/dist/core/auth/record-kinds.js +35 -0
- package/dist/core/auth/share-token.service.js +174 -122
- package/dist/core/capability/capability.service.js +1 -5
- package/dist/core/config/config.schema.js +1 -6
- package/dist/core/config/config.service.js +2 -6
- package/dist/core/events/event-bus.service.js +3 -7
- package/dist/core/feature/feature.service.js +2 -6
- package/dist/core/lifecycle/lifecycle-runner.singleton.js +5 -10
- package/dist/core/lifecycle/lifecycle-state-machine.js +1 -5
- package/dist/core/logging/log-ring-buffer.js +1 -5
- package/dist/core/logging/logging.service.js +5 -9
- package/dist/core/logging/scoped-logger.js +1 -5
- package/dist/core/moleculer/agent-readiness-pull.js +1 -4
- package/dist/core/moleculer/agent-registration-request.js +3 -7
- package/dist/core/moleculer/cap-call-fn.js +39 -5
- package/dist/core/moleculer/cap-route-authority.js +3 -8
- package/dist/core/moleculer/hub-mesh-guard.js +5 -9
- package/dist/core/moleculer/moleculer.service.js +108 -119
- package/dist/core/network/network-quality.service.js +2 -6
- package/dist/core/notification/notification-wrapper.service.js +3 -7
- package/dist/core/notification/toast-wrapper.service.js +3 -7
- package/dist/core/provider/provider.tokens.js +1 -4
- package/dist/core/repl/repl-engine.service.js +4 -8
- package/dist/core/runner-health/runner-boot-check.js +7 -15
- package/dist/core/runner-health/update-preflight.js +9 -14
- package/dist/core/server-update/server-update.service.js +12 -49
- package/dist/core/server-update/system-apply-closure-patches.js +6 -42
- package/dist/core/server-update/system-ensure-prebuilds.js +3 -6
- package/dist/core/server-update/system-exec-npm.js +2 -5
- package/dist/core/storage/fs-storage-backend.js +1 -5
- package/dist/core/storage/storage-location-manager.js +1 -5
- package/dist/core/storage/storage.service.js +2 -6
- package/dist/core/streaming/ffmpeg-stream-info.js +3 -8
- package/dist/core/streaming/stream-probe.service.js +12 -16
- package/dist/core/streaming/ttl-cache.js +1 -5
- package/dist/core/topology/topology-emitter.service.js +19 -23
- package/dist/core/update-availability-emitter.js +3 -7
- package/dist/core/update-availability-store.js +8 -45
- package/dist/core/updates/agent-installed-packages.js +5 -9
- package/dist/core/updates/restart-marker.js +3 -8
- package/dist/core/updates/update-check-scheduler.js +13 -18
- package/dist/core/updates/wrapper-update-checker.js +3 -7
- package/dist/first-boot-addon-plan.js +2 -6
- package/dist/framework-nodepath.js +2 -38
- package/dist/i18n/request-lang.js +7 -11
- package/dist/launcher.js +30 -65
- package/dist/lifecycle-journal-path.js +2 -38
- package/dist/main.js +287 -324
- package/dist/manual-boot.js +120 -157
- package/dist/node-role.js +1 -4
- package/dist/package-inventory.js +7 -48
- package/dist/pending-restore.js +7 -43
- package/dist/single-copy-cleanup-runner.js +9 -45
- package/dist/single-copy-cleanup.js +7 -15
- package/dist/sqlite-restore.js +9 -48
- package/dist/starter.js +4 -39
- package/dist/ui-serving/lazy-ui-serving.js +3 -39
- package/package.json +13 -13
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/* eslint-disable @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access -- pre-existing lint debt across this 800-line provider-factory module. The flagged sites delegate into Moleculer/EventBus/IntegrationRegistry surfaces typed as `unknown` to break circular dependencies; runtime contracts are validated by the cap-mount-helper layer above. Tracked separately. */
|
|
3
2
|
/**
|
|
4
3
|
* Capability provider factories for the Phase E core caps —
|
|
@@ -24,80 +23,34 @@
|
|
|
24
23
|
* `server/backend/src/api/core/{system,network-quality,toast,
|
|
25
24
|
* nodes,integrations,addons}.router.ts`.
|
|
26
25
|
*/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
var ar = [];
|
|
47
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
48
|
-
return ar;
|
|
49
|
-
};
|
|
50
|
-
return ownKeys(o);
|
|
51
|
-
};
|
|
52
|
-
return function (mod) {
|
|
53
|
-
if (mod && mod.__esModule) return mod;
|
|
54
|
-
var result = {};
|
|
55
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
56
|
-
__setModuleDefault(result, mod);
|
|
57
|
-
return result;
|
|
58
|
-
};
|
|
59
|
-
})();
|
|
60
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
61
|
-
exports.INTEGRATION_CAP_MARKERS = void 0;
|
|
62
|
-
exports.buildSystemProvider = buildSystemProvider;
|
|
63
|
-
exports.buildSettingsStoreGateway = buildSettingsStoreGateway;
|
|
64
|
-
exports.buildNetworkQualityProvider = buildNetworkQualityProvider;
|
|
65
|
-
exports.buildToastProvider = buildToastProvider;
|
|
66
|
-
exports.computeTopologyPass = computeTopologyPass;
|
|
67
|
-
exports.computeTopology = computeTopology;
|
|
68
|
-
exports.createNodeRootPackageLookup = createNodeRootPackageLookup;
|
|
69
|
-
exports.buildNodesProvider = buildNodesProvider;
|
|
70
|
-
exports.buildIntegrationsProvider = buildIntegrationsProvider;
|
|
71
|
-
exports.dispatchCustomAction = dispatchCustomAction;
|
|
72
|
-
exports.buildAddonsProvider = buildAddonsProvider;
|
|
73
|
-
const integration_secrets_1 = require("./integration-secrets");
|
|
74
|
-
const node_crypto_1 = require("node:crypto");
|
|
75
|
-
const os = __importStar(require("node:os"));
|
|
76
|
-
const system_1 = require("@camstack/system");
|
|
77
|
-
const types_1 = require("@camstack/types");
|
|
78
|
-
const server_1 = require("@trpc/server");
|
|
79
|
-
const integration_id_backfill_1 = require("../../boot/integration-id-backfill");
|
|
80
|
-
const addon_package_service_js_1 = require("../../core/addon/addon-package.service.js");
|
|
81
|
-
const addon_uninstall_js_1 = require("../../core/addon/addon-uninstall.js");
|
|
82
|
-
const lifecycle_runner_singleton_js_1 = require("../../core/lifecycle/lifecycle-runner.singleton.js");
|
|
83
|
-
const agent_installed_packages_js_1 = require("../../core/updates/agent-installed-packages.js");
|
|
84
|
-
const http_request_census_singleton_js_1 = require("../trpc/http-request-census.singleton.js");
|
|
85
|
-
const capability_preference_writes_js_1 = require("./capability-preference-writes.js");
|
|
86
|
-
const failure_contributions_js_1 = require("./failure-contributions.js");
|
|
87
|
-
const load_contributions_js_1 = require("./load-contributions.js");
|
|
88
|
-
const logging_settings_js_1 = require("./logging-settings.js");
|
|
89
|
-
const request_census_settings_js_1 = require("./request-census-settings.js");
|
|
90
|
-
const server_name_js_1 = require("./server-name.js");
|
|
91
|
-
const site_location_js_1 = require("./site-location.js");
|
|
26
|
+
import { readIntegrationSettings, writeIntegrationSettings } from './integration-secrets';
|
|
27
|
+
import { randomUUID } from 'node:crypto';
|
|
28
|
+
import * as os from 'node:os';
|
|
29
|
+
import { asBrokerLike, getCapUsageRegistry, getLoggingGate, getProcessNodeMeshTransport, IntegrationRegistry, } from '@camstack/system';
|
|
30
|
+
import { EventCategory, errMsg } from '@camstack/types';
|
|
31
|
+
import { TRPCError } from '@trpc/server';
|
|
32
|
+
import { planDeleteTimeStamps } from '../../boot/integration-id-backfill';
|
|
33
|
+
import { SYSTEM_PACKAGE } from '../../core/addon/addon-package.service.js';
|
|
34
|
+
import { listInstallations, PackageProtectedError, uninstallPackage, } from '../../core/addon/addon-uninstall.js';
|
|
35
|
+
import { getLifecycleRunner } from '../../core/lifecycle/lifecycle-runner.singleton.js';
|
|
36
|
+
import { AGENT_STATUS_TIMEOUT_MS, fetchAgentInstalledPackages, } from '../../core/updates/agent-installed-packages.js';
|
|
37
|
+
import { getHttpRequestCensus } from '../trpc/http-request-census.singleton.js';
|
|
38
|
+
import { writeCollectionToggle } from './capability-preference-writes.js';
|
|
39
|
+
import { buildFailureContributionPlane } from './failure-contributions.js';
|
|
40
|
+
import { buildLoadContributionPlane } from './load-contributions.js';
|
|
41
|
+
import { buildLogChannelPlane, LoggingSettingsService } from './logging-settings.js';
|
|
42
|
+
import { RequestCensusService } from './request-census-settings.js';
|
|
43
|
+
import { ServerNameReader } from './server-name.js';
|
|
44
|
+
import { SiteLocationService } from './site-location.js';
|
|
92
45
|
// ── system ──────────────────────────────────────────────────────────
|
|
93
46
|
function getRetention(registry) {
|
|
94
47
|
return (registry?.getSingleton('analysis-data-persistence')?.retention ?? null);
|
|
95
48
|
}
|
|
96
|
-
function buildSystemProvider(feature, registry, logger, census =
|
|
49
|
+
export function buildSystemProvider(feature, registry, logger, census = getHttpRequestCensus()) {
|
|
97
50
|
const siteLocation = buildSiteLocationService(registry, logger);
|
|
98
51
|
const store = buildSettingsStoreGateway(registry);
|
|
99
|
-
const serverName = new
|
|
100
|
-
const requestCensus = new
|
|
52
|
+
const serverName = new ServerNameReader({ store, ...(logger !== undefined ? { logger } : {}) });
|
|
53
|
+
const requestCensus = new RequestCensusService({
|
|
101
54
|
census,
|
|
102
55
|
store,
|
|
103
56
|
...(logger !== undefined ? { logger } : {}),
|
|
@@ -109,24 +62,24 @@ function buildSystemProvider(feature, registry, logger, census = (0, http_reques
|
|
|
109
62
|
// The channels themselves are DECLARED by the addons that own them and
|
|
110
63
|
// reached through the registry's per-provider proxies — hub-main never keeps
|
|
111
64
|
// a hand-written list, which would rot at the first addon that adds one.
|
|
112
|
-
const channels =
|
|
65
|
+
const channels = buildLogChannelPlane({ entries: () => registry?.getCollectionEntries('log-channels') ?? [] }, (addonId, phase, error) => {
|
|
113
66
|
logger?.warn('log-channels provider unreachable', { meta: { addonId, phase, error } });
|
|
114
67
|
});
|
|
115
68
|
// Every addon's OWN reported cost, assembled by enumeration. Nothing here
|
|
116
69
|
// knows which pid belongs to which camera — the addon that spawned the child
|
|
117
70
|
// does, and it is the one that says so.
|
|
118
|
-
const loadContributions =
|
|
71
|
+
const loadContributions = buildLoadContributionPlane({ entries: () => registry?.getCollectionEntries('load-contribution') ?? [] }, (addonId, error) => {
|
|
119
72
|
logger?.warn('load-contribution provider unreachable', { meta: { addonId, error } });
|
|
120
73
|
});
|
|
121
74
|
// Every addon's OWN reported losses, per camera, with the denominator
|
|
122
75
|
// attached. Same enumeration, same reason: the addon that owns the failing
|
|
123
76
|
// path is the only thing that knows which camera it lost work for.
|
|
124
|
-
const failureContributions =
|
|
77
|
+
const failureContributions = buildFailureContributionPlane({ entries: () => registry?.getCollectionEntries('failure-contribution') ?? [] }, (addonId, error) => {
|
|
125
78
|
logger?.warn('failure-contribution provider unreachable', { meta: { addonId, error } });
|
|
126
79
|
});
|
|
127
|
-
const loggingSettings = new
|
|
80
|
+
const loggingSettings = new LoggingSettingsService({
|
|
128
81
|
store,
|
|
129
|
-
gate:
|
|
82
|
+
gate: getLoggingGate(),
|
|
130
83
|
requestCensus,
|
|
131
84
|
channels,
|
|
132
85
|
...(logger !== undefined ? { logger } : {}),
|
|
@@ -181,7 +134,7 @@ function buildSystemProvider(feature, registry, logger, census = (0, http_reques
|
|
|
181
134
|
* invent a location out of a missing dependency.
|
|
182
135
|
*/
|
|
183
136
|
function buildSiteLocationService(registry, logger) {
|
|
184
|
-
return new
|
|
137
|
+
return new SiteLocationService({
|
|
185
138
|
store: buildSettingsStoreGateway(registry),
|
|
186
139
|
...(logger !== undefined ? { logger } : {}),
|
|
187
140
|
});
|
|
@@ -197,7 +150,7 @@ function buildSiteLocationService(registry, logger) {
|
|
|
197
150
|
* every call throws, so a caller cannot mistake a missing dependency for an
|
|
198
151
|
* empty row.
|
|
199
152
|
*/
|
|
200
|
-
function buildSettingsStoreGateway(registry) {
|
|
153
|
+
export function buildSettingsStoreGateway(registry) {
|
|
201
154
|
const resolve = () => {
|
|
202
155
|
const provider = registry?.getSingleton('settings-store') ?? null;
|
|
203
156
|
if (provider === null)
|
|
@@ -212,7 +165,7 @@ function buildSettingsStoreGateway(registry) {
|
|
|
212
165
|
};
|
|
213
166
|
}
|
|
214
167
|
// ── network-quality ─────────────────────────────────────────────────
|
|
215
|
-
function buildNetworkQualityProvider(nq) {
|
|
168
|
+
export function buildNetworkQualityProvider(nq) {
|
|
216
169
|
return {
|
|
217
170
|
getDeviceStats: async (input) => nq.getDeviceStats(input.deviceId),
|
|
218
171
|
getAllStats: async () => nq.getAllStats(),
|
|
@@ -235,13 +188,13 @@ function buildNetworkQualityProvider(nq) {
|
|
|
235
188
|
// can scope deliveries (broadcast vs. per-user). The cap defines
|
|
236
189
|
// `onToast` as a subscription — the codegen wires it through the
|
|
237
190
|
// `iterableSubscription` adapter and pushes raw `Toast` payloads.
|
|
238
|
-
function buildToastProvider(toastService, ctx) {
|
|
191
|
+
export function buildToastProvider(toastService, ctx) {
|
|
239
192
|
return {
|
|
240
193
|
onToast: (_input, push) => {
|
|
241
194
|
if (!toastService)
|
|
242
195
|
return () => { };
|
|
243
196
|
const userId = ctx.user?.id ?? 'anonymous';
|
|
244
|
-
const connectionId =
|
|
197
|
+
const connectionId = randomUUID();
|
|
245
198
|
const unsubscribe = toastService.subscribe(connectionId, userId, (toast) => push(toast));
|
|
246
199
|
return unsubscribe ?? (() => { });
|
|
247
200
|
},
|
|
@@ -249,7 +202,7 @@ function buildToastProvider(toastService, ctx) {
|
|
|
249
202
|
}
|
|
250
203
|
/** `{ meshTransport }` for an online agent, `{}` when the hub cannot tell. */
|
|
251
204
|
function meshTransportField(nodeId) {
|
|
252
|
-
const meshTransport =
|
|
205
|
+
const meshTransport = getProcessNodeMeshTransport(nodeId);
|
|
253
206
|
return meshTransport === undefined ? {} : { meshTransport };
|
|
254
207
|
}
|
|
255
208
|
function getLocalIps() {
|
|
@@ -267,11 +220,11 @@ function getLocalIps() {
|
|
|
267
220
|
return ips;
|
|
268
221
|
}
|
|
269
222
|
/** {@link computeTopology}, keeping the live entries the projection consumed. */
|
|
270
|
-
async function computeTopologyPass(agentRegistry, addonRegistry, getNodeRootPackage, isNodeRegistered) {
|
|
223
|
+
export async function computeTopologyPass(agentRegistry, addonRegistry, getNodeRootPackage, isNodeRegistered) {
|
|
271
224
|
const [nodes, liveNodes] = await computeTopologyInternal(agentRegistry, addonRegistry, getNodeRootPackage, isNodeRegistered);
|
|
272
225
|
return { nodes, liveNodes };
|
|
273
226
|
}
|
|
274
|
-
async function computeTopology(agentRegistry, addonRegistry, getNodeRootPackage, isNodeRegistered) {
|
|
227
|
+
export async function computeTopology(agentRegistry, addonRegistry, getNodeRootPackage, isNodeRegistered) {
|
|
275
228
|
const [nodes] = await computeTopologyInternal(agentRegistry, addonRegistry, getNodeRootPackage, isNodeRegistered);
|
|
276
229
|
return nodes;
|
|
277
230
|
}
|
|
@@ -476,7 +429,7 @@ async function computeTopologyInternal(agentRegistry, addonRegistry, getNodeRoot
|
|
|
476
429
|
* running `@camstack/server` package (ServerUpdateService), agent rows from
|
|
477
430
|
* the `registerNode`-fed HubNodeRegistry (via MoleculerService).
|
|
478
431
|
*/
|
|
479
|
-
function createNodeRootPackageLookup(moleculer, serverUpdate) {
|
|
432
|
+
export function createNodeRootPackageLookup(moleculer, serverUpdate) {
|
|
480
433
|
return (nodeId, isHub) => {
|
|
481
434
|
if (isHub) {
|
|
482
435
|
const running = serverUpdate?.getRunningRootPackage();
|
|
@@ -487,8 +440,8 @@ function createNodeRootPackageLookup(moleculer, serverUpdate) {
|
|
|
487
440
|
return moleculer.getNodeRootPackage(nodeId);
|
|
488
441
|
};
|
|
489
442
|
}
|
|
490
|
-
function buildNodesProvider(agentRegistry, moleculer, addonRegistry, getNodeRootPackage, logger) {
|
|
491
|
-
const broker = moleculer.broker;
|
|
443
|
+
export function buildNodesProvider(agentRegistry, moleculer, addonRegistry, getNodeRootPackage, logger) {
|
|
444
|
+
const broker = asBrokerLike(moleculer.broker);
|
|
492
445
|
return {
|
|
493
446
|
topology: async () => computeTopology(agentRegistry, addonRegistry, getNodeRootPackage, (nodeId) => moleculer.isNodeRegistered(nodeId)),
|
|
494
447
|
directory: async () => agentRegistry.listNodeDirectory(),
|
|
@@ -675,7 +628,7 @@ function buildNodesProvider(agentRegistry, moleculer, addonRegistry, getNodeRoot
|
|
|
675
628
|
* `LocalChildRegistryOptions.capUsageObserver`.
|
|
676
629
|
*/
|
|
677
630
|
getCapUsageGraph: async (input) => {
|
|
678
|
-
const reg =
|
|
631
|
+
const reg = getCapUsageRegistry();
|
|
679
632
|
const stats = reg.getStats();
|
|
680
633
|
if (stats.droppedTriples > 0) {
|
|
681
634
|
// The registry is bounded (it lives on hub-main — D181). If it ever
|
|
@@ -793,11 +746,11 @@ async function runConnectionTest(ar, addonId, settings, logger) {
|
|
|
793
746
|
catch (err) {
|
|
794
747
|
logger.warn('connection test could not run', {
|
|
795
748
|
tags: { addonId },
|
|
796
|
-
meta: { phase: 'test', error:
|
|
749
|
+
meta: { phase: 'test', error: errMsg(err) },
|
|
797
750
|
});
|
|
798
751
|
return {
|
|
799
752
|
status: 'inconclusive',
|
|
800
|
-
error: `Connection test could not run: ${
|
|
753
|
+
error: `Connection test could not run: ${errMsg(err)}`,
|
|
801
754
|
testedBy: addonId,
|
|
802
755
|
};
|
|
803
756
|
}
|
|
@@ -827,11 +780,11 @@ async function runConnectionTest(ar, addonId, settings, logger) {
|
|
|
827
780
|
catch (err) {
|
|
828
781
|
logger.warn('broker connection test could not run', {
|
|
829
782
|
tags: { addonId },
|
|
830
|
-
meta: { phase: 'test', brokerId, error:
|
|
783
|
+
meta: { phase: 'test', brokerId, error: errMsg(err) },
|
|
831
784
|
});
|
|
832
785
|
return {
|
|
833
786
|
status: 'inconclusive',
|
|
834
|
-
error: `Broker test could not run: ${
|
|
787
|
+
error: `Broker test could not run: ${errMsg(err)}`,
|
|
835
788
|
testedBy: addonId,
|
|
836
789
|
};
|
|
837
790
|
}
|
|
@@ -859,8 +812,8 @@ async function runConnectionTest(ar, addonId, settings, logger) {
|
|
|
859
812
|
* Exported so the integration-markers spec can assert the recognised set
|
|
860
813
|
* without booting the whole provider factory.
|
|
861
814
|
*/
|
|
862
|
-
|
|
863
|
-
function buildIntegrationsProvider(ar, eb, loggingService, capabilityRegistry) {
|
|
815
|
+
export const INTEGRATION_CAP_MARKERS = new Set(['device-provider', 'device-adoption']);
|
|
816
|
+
export function buildIntegrationsProvider(ar, eb, loggingService, capabilityRegistry) {
|
|
864
817
|
const logger = loggingService.createLogger('integrations');
|
|
865
818
|
const withProcessState = (i) => ({
|
|
866
819
|
...i,
|
|
@@ -915,7 +868,7 @@ function buildIntegrationsProvider(ar, eb, loggingService, capabilityRegistry) {
|
|
|
915
868
|
tags: { addonId: input.addonId },
|
|
916
869
|
meta: { phase: 'create', status: verdict.status, testedBy: verdict.testedBy },
|
|
917
870
|
});
|
|
918
|
-
throw new
|
|
871
|
+
throw new TRPCError({
|
|
919
872
|
code: 'BAD_REQUEST',
|
|
920
873
|
message: verdict.error ?? `Connection test failed for '${input.addonId}'`,
|
|
921
874
|
});
|
|
@@ -934,7 +887,7 @@ function buildIntegrationsProvider(ar, eb, loggingService, capabilityRegistry) {
|
|
|
934
887
|
}
|
|
935
888
|
const integration = await reg.createIntegration(payload);
|
|
936
889
|
if (settings !== undefined && Object.keys(settings).length > 0) {
|
|
937
|
-
await
|
|
890
|
+
await writeIntegrationSettings({
|
|
938
891
|
registry: reg,
|
|
939
892
|
secrets: ownerSecrets(ar, integration.addonId),
|
|
940
893
|
onOrphaned: (refs) => logger.warn('dropped keychain refs could not be deleted', {
|
|
@@ -1044,7 +997,7 @@ function buildIntegrationsProvider(ar, eb, loggingService, capabilityRegistry) {
|
|
|
1044
997
|
// alarm panel (835) one second before the registry answered "is fixed",
|
|
1045
998
|
// and the panel came back with a new id and no state, silently breaking
|
|
1046
999
|
// every rule that named it.
|
|
1047
|
-
if (
|
|
1000
|
+
if (IntegrationRegistry.isFixed(integration)) {
|
|
1048
1001
|
throw new Error(`integration "${input.id}" is fixed — it belongs to the addon that declared it and cannot be deleted`);
|
|
1049
1002
|
}
|
|
1050
1003
|
logger.info('removing', {
|
|
@@ -1068,7 +1021,7 @@ function buildIntegrationsProvider(ar, eb, loggingService, capabilityRegistry) {
|
|
|
1068
1021
|
reg.listIntegrations(),
|
|
1069
1022
|
dm.listAll({}),
|
|
1070
1023
|
]);
|
|
1071
|
-
const stamps =
|
|
1024
|
+
const stamps = planDeleteTimeStamps(input.id, integrations.map((i) => ({ id: i.id, addonId: i.addonId })), devices.map((d) => ({
|
|
1072
1025
|
id: d.id,
|
|
1073
1026
|
addonId: d.addonId,
|
|
1074
1027
|
parentDeviceId: d.parentDeviceId,
|
|
@@ -1090,7 +1043,7 @@ function buildIntegrationsProvider(ar, eb, loggingService, capabilityRegistry) {
|
|
|
1090
1043
|
catch (err) {
|
|
1091
1044
|
logger.warn('legacy device claim failed (best-effort — continuing)', {
|
|
1092
1045
|
tags: { integrationId: input.id },
|
|
1093
|
-
meta: { phase: 'delete', error:
|
|
1046
|
+
meta: { phase: 'delete', error: errMsg(err) },
|
|
1094
1047
|
});
|
|
1095
1048
|
}
|
|
1096
1049
|
}
|
|
@@ -1105,7 +1058,7 @@ function buildIntegrationsProvider(ar, eb, loggingService, capabilityRegistry) {
|
|
|
1105
1058
|
catch (err) {
|
|
1106
1059
|
logger.warn('device cascade-remove failed (best-effort — continuing)', {
|
|
1107
1060
|
tags: { integrationId: input.id },
|
|
1108
|
-
meta: { phase: 'delete', error:
|
|
1061
|
+
meta: { phase: 'delete', error: errMsg(err) },
|
|
1109
1062
|
});
|
|
1110
1063
|
}
|
|
1111
1064
|
}
|
|
@@ -1120,7 +1073,7 @@ function buildIntegrationsProvider(ar, eb, loggingService, capabilityRegistry) {
|
|
|
1120
1073
|
id: `integration-deleted-${Date.now()}`,
|
|
1121
1074
|
timestamp: new Date(),
|
|
1122
1075
|
source: { type: 'integration', id: input.id },
|
|
1123
|
-
category:
|
|
1076
|
+
category: EventCategory.IntegrationDeleted,
|
|
1124
1077
|
data: {
|
|
1125
1078
|
integrationId: input.id,
|
|
1126
1079
|
addonId: integration.addonId,
|
|
@@ -1143,7 +1096,7 @@ function buildIntegrationsProvider(ar, eb, loggingService, capabilityRegistry) {
|
|
|
1143
1096
|
const caller = Reflect.get(input, 'callerAddonId');
|
|
1144
1097
|
const forOwner = typeof caller === 'string' &&
|
|
1145
1098
|
(caller === integration.addonId || ar.runnerIdFor(integration.addonId) === caller);
|
|
1146
|
-
return
|
|
1099
|
+
return readIntegrationSettings({ registry: reg, secrets: forOwner ? ownerSecrets(ar, integration.addonId) : null }, { integrationId: input.id, forOwner });
|
|
1147
1100
|
},
|
|
1148
1101
|
setSettings: async (input) => {
|
|
1149
1102
|
const reg = requireIntegrationRegistry(ar);
|
|
@@ -1160,7 +1113,7 @@ function buildIntegrationsProvider(ar, eb, loggingService, capabilityRegistry) {
|
|
|
1160
1113
|
tags: { integrationId: input.id, addonId: integration.addonId },
|
|
1161
1114
|
meta: { phase: 'setSettings', keys: settingsKeys },
|
|
1162
1115
|
});
|
|
1163
|
-
await
|
|
1116
|
+
await writeIntegrationSettings({
|
|
1164
1117
|
registry: reg,
|
|
1165
1118
|
secrets: ownerSecrets(ar, integration.addonId),
|
|
1166
1119
|
onOrphaned: (refs) => logger.warn('dropped keychain refs could not be deleted', {
|
|
@@ -1199,7 +1152,7 @@ function buildIntegrationsProvider(ar, eb, loggingService, capabilityRegistry) {
|
|
|
1199
1152
|
const providerAddons = addons.filter((a) => a.process?.state !== 'failed' &&
|
|
1200
1153
|
a.manifest.capabilities?.some((c) => {
|
|
1201
1154
|
const name = typeof c === 'string' ? c : c.name;
|
|
1202
|
-
return typeof name === 'string' &&
|
|
1155
|
+
return typeof name === 'string' && INTEGRATION_CAP_MARKERS.has(name);
|
|
1203
1156
|
}));
|
|
1204
1157
|
const integrations = await reg.listIntegrations();
|
|
1205
1158
|
return providerAddons.map((addon) => {
|
|
@@ -1294,13 +1247,13 @@ function ensureCustomActionAuth(ctx, level) {
|
|
|
1294
1247
|
if (level === 'public')
|
|
1295
1248
|
return;
|
|
1296
1249
|
if (!ctx.user) {
|
|
1297
|
-
throw new
|
|
1250
|
+
throw new TRPCError({ code: 'UNAUTHORIZED' });
|
|
1298
1251
|
}
|
|
1299
1252
|
if (level === 'protected')
|
|
1300
1253
|
return;
|
|
1301
1254
|
if (level === 'admin') {
|
|
1302
1255
|
if (!ctx.user.isAdmin) {
|
|
1303
|
-
throw new
|
|
1256
|
+
throw new TRPCError({ code: 'FORBIDDEN', message: 'custom action requires admin' });
|
|
1304
1257
|
}
|
|
1305
1258
|
return;
|
|
1306
1259
|
}
|
|
@@ -1357,10 +1310,10 @@ function formatNames(names) {
|
|
|
1357
1310
|
* trusted from input).
|
|
1358
1311
|
* 5. Validate the addon's output — crash-early on a misbehaving addon.
|
|
1359
1312
|
*/
|
|
1360
|
-
async function dispatchCustomAction(registry, ctx, input) {
|
|
1313
|
+
export async function dispatchCustomAction(registry, ctx, input) {
|
|
1361
1314
|
const entry = registry.resolve(input.addonId, input.action);
|
|
1362
1315
|
if (!entry) {
|
|
1363
|
-
throw new
|
|
1316
|
+
throw new TRPCError({ code: 'NOT_FOUND', message: describeUnresolved(registry, input) });
|
|
1364
1317
|
}
|
|
1365
1318
|
ensureCustomActionAuth(ctx, entry.spec.auth);
|
|
1366
1319
|
const parsedInput = entry.spec.input.parse(input.input);
|
|
@@ -1450,7 +1403,7 @@ function buildUninstallSeam(ar, ps, broker, agentRegistry) {
|
|
|
1450
1403
|
.filter((n) => !n.isHub && !n.id.includes('/'))
|
|
1451
1404
|
.map((n) => ({ id: n.id, name: n.name, isOnline: n.isOnline }));
|
|
1452
1405
|
},
|
|
1453
|
-
agentAddons: async (nodeId) => parseAgentStatusAddons(await broker.call('$agent.status', {}, { nodeID: nodeId, timeout:
|
|
1406
|
+
agentAddons: async (nodeId) => parseAgentStatusAddons(await broker.call('$agent.status', {}, { nodeID: nodeId, timeout: AGENT_STATUS_TIMEOUT_MS })),
|
|
1454
1407
|
agentUndeploy: (nodeId, request) => broker.call('$agent.undeploy', request, {
|
|
1455
1408
|
nodeID: nodeId,
|
|
1456
1409
|
timeout: AGENT_UNDEPLOY_TIMEOUT_MS,
|
|
@@ -1461,13 +1414,13 @@ function buildUninstallSeam(ar, ps, broker, agentRegistry) {
|
|
|
1461
1414
|
},
|
|
1462
1415
|
};
|
|
1463
1416
|
}
|
|
1464
|
-
function buildAddonsProvider(ar, ps, ls, moleculer, configService, ctx, agentRegistry) {
|
|
1465
|
-
const broker = moleculer.broker;
|
|
1417
|
+
export function buildAddonsProvider(ar, ps, ls, moleculer, configService, ctx, agentRegistry) {
|
|
1418
|
+
const broker = asBrokerLike(moleculer.broker);
|
|
1466
1419
|
const uninstallSeam = buildUninstallSeam(ar, ps, broker, agentRegistry);
|
|
1467
|
-
const frameworkAllowSet = new Set([
|
|
1420
|
+
const frameworkAllowSet = new Set([SYSTEM_PACKAGE]);
|
|
1468
1421
|
// Process-wide runner built once at boot (manual-boot wires its deps from the
|
|
1469
1422
|
// addon-package service + event bus). `createdBy` travels per `startJob` call.
|
|
1470
|
-
const lifecycleRunner =
|
|
1423
|
+
const lifecycleRunner = getLifecycleRunner();
|
|
1471
1424
|
// Who triggered each lifecycle job from this request context.
|
|
1472
1425
|
const lifecycleCreatedBy = ctx.user?.username ?? ctx.user?.id ?? 'system';
|
|
1473
1426
|
return {
|
|
@@ -1490,7 +1443,7 @@ function buildAddonsProvider(ar, ps, ls, moleculer, configService, ctx, agentReg
|
|
|
1490
1443
|
}
|
|
1491
1444
|
// Agent: same roster source as listUpdates — stamp agent versions onto
|
|
1492
1445
|
// hub manifests so cards show what that node actually runs.
|
|
1493
|
-
const agentPackages = await
|
|
1446
|
+
const agentPackages = await fetchAgentInstalledPackages(broker, nodeId);
|
|
1494
1447
|
const versionByPackage = new Map();
|
|
1495
1448
|
for (const pkg of agentPackages) {
|
|
1496
1449
|
versionByPackage.set(pkg.name, pkg.version);
|
|
@@ -1527,16 +1480,16 @@ function buildAddonsProvider(ar, ps, ls, moleculer, configService, ctx, agentReg
|
|
|
1527
1480
|
listWorkspacePackages: async () => ps.listWorkspacePackages(),
|
|
1528
1481
|
uninstallPackage: async (input) => {
|
|
1529
1482
|
try {
|
|
1530
|
-
return await
|
|
1483
|
+
return await uninstallPackage(uninstallSeam, input);
|
|
1531
1484
|
}
|
|
1532
1485
|
catch (err) {
|
|
1533
|
-
if (err instanceof
|
|
1534
|
-
throw new
|
|
1486
|
+
if (err instanceof PackageProtectedError) {
|
|
1487
|
+
throw new TRPCError({ code: 'FORBIDDEN', message: err.message });
|
|
1535
1488
|
}
|
|
1536
1489
|
throw err;
|
|
1537
1490
|
}
|
|
1538
1491
|
},
|
|
1539
|
-
listInstallations: async (input) =>
|
|
1492
|
+
listInstallations: async (input) => listInstallations(uninstallSeam, input.packageName),
|
|
1540
1493
|
reloadPackages: async () => ps.reloadPackages(),
|
|
1541
1494
|
searchAvailable: async (input) => {
|
|
1542
1495
|
const results = await ps.searchNpm(input?.query, input?.capability);
|
|
@@ -1550,7 +1503,7 @@ function buildAddonsProvider(ar, ps, ls, moleculer, configService, ctx, agentReg
|
|
|
1550
1503
|
const nodeId = input.nodeId;
|
|
1551
1504
|
const updates = nodeId === undefined || isHubNode(nodeId)
|
|
1552
1505
|
? await ps.checkUpdates()
|
|
1553
|
-
: await ps.checkUpdatesForInstalled(await
|
|
1506
|
+
: await ps.checkUpdatesForInstalled(await fetchAgentInstalledPackages(broker, nodeId), nodeId);
|
|
1554
1507
|
return updates.map((u) => ({ ...u, isSystem: frameworkAllowSet.has(u.name) }));
|
|
1555
1508
|
},
|
|
1556
1509
|
updatePackage: async (input) => {
|
|
@@ -1604,7 +1557,7 @@ function buildAddonsProvider(ar, ps, ls, moleculer, configService, ctx, agentReg
|
|
|
1604
1557
|
if (nodeId === undefined || isHubNode(nodeId))
|
|
1605
1558
|
return ps.checkUpdates(true);
|
|
1606
1559
|
// Agent rosters carry no hub-side cache — the diff is always live.
|
|
1607
|
-
const installed = await
|
|
1560
|
+
const installed = await fetchAgentInstalledPackages(broker, nodeId);
|
|
1608
1561
|
return ps.checkUpdatesForInstalled(installed, nodeId);
|
|
1609
1562
|
},
|
|
1610
1563
|
restartServer: async () => ps.restartServer(ctx.user?.username ?? ctx.user?.id),
|
|
@@ -1612,7 +1565,7 @@ function buildAddonsProvider(ar, ps, ls, moleculer, configService, ctx, agentReg
|
|
|
1612
1565
|
// Avoid pulling PostBootService through the cap-providers tree —
|
|
1613
1566
|
// dynamic import keeps the wiring loose and lets us read the
|
|
1614
1567
|
// static cache without a constructor dependency.
|
|
1615
|
-
const mod = await
|
|
1568
|
+
const mod = await import('../../boot/post-boot.service.js');
|
|
1616
1569
|
return mod.PostBootService.getLastRestart();
|
|
1617
1570
|
},
|
|
1618
1571
|
listFrameworkPackages: async () => ps.listFrameworkPackages(),
|
|
@@ -1634,7 +1587,7 @@ function buildAddonsProvider(ar, ps, ls, moleculer, configService, ctx, agentReg
|
|
|
1634
1587
|
// Refused (unknown cap, not a collection, unregistered provider) before
|
|
1635
1588
|
// anything is written; otherwise the new disabled-set is PERSISTED first
|
|
1636
1589
|
// and applied live only once the write landed (D675 amendment).
|
|
1637
|
-
await
|
|
1590
|
+
await writeCollectionToggle(configService, ar.getCapabilityRegistry(), {
|
|
1638
1591
|
capability: input.capName,
|
|
1639
1592
|
addonId: input.addonId,
|
|
1640
1593
|
enabled: input.enabled,
|
|
@@ -1671,7 +1624,7 @@ function buildAddonsProvider(ar, ps, ls, moleculer, configService, ctx, agentReg
|
|
|
1671
1624
|
// which has a real `$agent.deploy` path.
|
|
1672
1625
|
const offNode = (input.nodeIds ?? []).filter((id) => !isHubNode(id));
|
|
1673
1626
|
if (offNode.length > 0) {
|
|
1674
|
-
throw new
|
|
1627
|
+
throw new TRPCError({
|
|
1675
1628
|
code: 'BAD_REQUEST',
|
|
1676
1629
|
message: input.kind === 'uninstall'
|
|
1677
1630
|
? `The lifecycle job engine is hub-local; it cannot target ${offNode.join(', ')}. Uninstall from an agent via uninstallPackage({ packageName, nodeId }), or from every node via uninstallPackage({ packageName, everywhere: true }).`
|
|
@@ -1,53 +1,50 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createCapabilitiesRouter = createCapabilitiesRouter;
|
|
4
1
|
/**
|
|
5
2
|
* Capability admin router — fixed core API (not a capability).
|
|
6
3
|
*
|
|
7
4
|
* Lets admins inspect the capability registry and set system/device
|
|
8
5
|
* overrides. Wraps `CapabilityRegistry` directly (kernel primitive).
|
|
9
6
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
function createCapabilitiesRouter(registry, config) {
|
|
14
|
-
return
|
|
15
|
-
listCapabilities:
|
|
16
|
-
.input(
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { trpcRouter, adminProcedure } from '../trpc/trpc.middleware.js';
|
|
9
|
+
import { writeCollectionEnabledSet, singletonPreferenceKey, writeSingletonPreference, } from './capability-preference-writes.js';
|
|
10
|
+
export function createCapabilitiesRouter(registry, config) {
|
|
11
|
+
return trpcRouter({
|
|
12
|
+
listCapabilities: adminProcedure
|
|
13
|
+
.input(z.void())
|
|
17
14
|
.query(() => registry?.listCapabilities() ?? []),
|
|
18
|
-
getCapability:
|
|
15
|
+
getCapability: adminProcedure.input(z.object({ name: z.string() })).query(({ input }) => {
|
|
19
16
|
if (!registry)
|
|
20
17
|
return null;
|
|
21
18
|
return (registry.listCapabilities().find((c) => c.name === input.name) ?? null);
|
|
22
19
|
}),
|
|
23
|
-
setActiveSingleton:
|
|
24
|
-
.input(
|
|
25
|
-
capability:
|
|
26
|
-
addonId:
|
|
27
|
-
nodeId:
|
|
20
|
+
setActiveSingleton: adminProcedure
|
|
21
|
+
.input(z.object({
|
|
22
|
+
capability: z.string(),
|
|
23
|
+
addonId: z.string(),
|
|
24
|
+
nodeId: z.string().optional(),
|
|
28
25
|
}))
|
|
29
|
-
.output(
|
|
26
|
+
.output(z.void())
|
|
30
27
|
.mutation(async ({ input }) => {
|
|
31
28
|
if (!registry)
|
|
32
29
|
throw new Error('Capability registry unavailable');
|
|
33
30
|
// Persist FIRST, under the key the boot restorer reads
|
|
34
31
|
// (`capabilities.singleton.<cap>` / `capabilities.singletonNode.<cap>.<node>`),
|
|
35
32
|
// then switch live: a write that fails changes nothing (D675 amendment).
|
|
36
|
-
await
|
|
33
|
+
await writeSingletonPreference(config, registry, {
|
|
37
34
|
capability: input.capability,
|
|
38
35
|
addonId: input.addonId,
|
|
39
36
|
...(input.nodeId !== undefined ? { nodeId: input.nodeId } : {}),
|
|
40
37
|
});
|
|
41
38
|
}),
|
|
42
|
-
clearSingletonNodeOverride:
|
|
43
|
-
.input(
|
|
44
|
-
.output(
|
|
39
|
+
clearSingletonNodeOverride: adminProcedure
|
|
40
|
+
.input(z.object({ capability: z.string(), nodeId: z.string() }))
|
|
41
|
+
.output(z.void())
|
|
45
42
|
.mutation(async ({ input }) => {
|
|
46
43
|
if (!registry)
|
|
47
44
|
throw new Error('Capability registry unavailable');
|
|
48
45
|
// Persist the cleared state as null FIRST so the boot restorer doesn't
|
|
49
46
|
// re-apply a stale override; only a write that landed clears it live.
|
|
50
|
-
await config.set(
|
|
47
|
+
await config.set(singletonPreferenceKey(input.capability, input.nodeId), null);
|
|
51
48
|
registry.clearSingletonNodeOverride(input.capability, input.nodeId);
|
|
52
49
|
}),
|
|
53
50
|
/**
|
|
@@ -57,39 +54,39 @@ function createCapabilitiesRouter(registry, config) {
|
|
|
57
54
|
* re-enabled. Other collections are untouched. Intended as the
|
|
58
55
|
* save endpoint for a bulk multi-select UI.
|
|
59
56
|
*/
|
|
60
|
-
setCollectionEnabledProviders:
|
|
61
|
-
.input(
|
|
62
|
-
capability:
|
|
63
|
-
enabledAddonIds:
|
|
57
|
+
setCollectionEnabledProviders: adminProcedure
|
|
58
|
+
.input(z.object({
|
|
59
|
+
capability: z.string(),
|
|
60
|
+
enabledAddonIds: z.array(z.string()),
|
|
64
61
|
}))
|
|
65
|
-
.output(
|
|
62
|
+
.output(z.void())
|
|
66
63
|
.mutation(async ({ input }) => {
|
|
67
64
|
if (!registry)
|
|
68
65
|
throw new Error('Capability registry unavailable');
|
|
69
66
|
// Persisted under `capabilities.collection.<cap>` BEFORE the live
|
|
70
67
|
// switch. It used to switch live only, so the operator's pick
|
|
71
68
|
// reverted at the next boot (D62's defect, D675 amendment).
|
|
72
|
-
await
|
|
69
|
+
await writeCollectionEnabledSet(config, registry, input.capability, input.enabledAddonIds);
|
|
73
70
|
}),
|
|
74
|
-
setDeviceCapability:
|
|
75
|
-
.input(
|
|
76
|
-
.output(
|
|
71
|
+
setDeviceCapability: adminProcedure
|
|
72
|
+
.input(z.object({ deviceId: z.string(), capability: z.string(), addonId: z.string() }))
|
|
73
|
+
.output(z.void())
|
|
77
74
|
.mutation(({ input }) => {
|
|
78
75
|
if (!registry)
|
|
79
76
|
throw new Error('Capability registry unavailable');
|
|
80
77
|
registry.setDeviceOverride(input.deviceId, input.capability, input.addonId);
|
|
81
78
|
}),
|
|
82
|
-
clearDeviceCapability:
|
|
83
|
-
.input(
|
|
84
|
-
.output(
|
|
79
|
+
clearDeviceCapability: adminProcedure
|
|
80
|
+
.input(z.object({ deviceId: z.string(), capability: z.string() }))
|
|
81
|
+
.output(z.void())
|
|
85
82
|
.mutation(({ input }) => {
|
|
86
83
|
if (!registry)
|
|
87
84
|
throw new Error('Capability registry unavailable');
|
|
88
85
|
registry.clearDeviceOverride(input.deviceId, input.capability);
|
|
89
86
|
}),
|
|
90
|
-
getDeviceCapabilities:
|
|
91
|
-
.input(
|
|
92
|
-
.output(
|
|
87
|
+
getDeviceCapabilities: adminProcedure
|
|
88
|
+
.input(z.object({ deviceId: z.string() }))
|
|
89
|
+
.output(z.record(z.string(), z.string()))
|
|
93
90
|
.query(({ input }) => {
|
|
94
91
|
if (!registry)
|
|
95
92
|
return {};
|