@camstack/server 1.2.592 → 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 +219 -221
- package/dist/api/trpc/generated-cap-routers.js +4878 -4726
- 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 +254 -197
- 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,29 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const agent_registration_request_js_1 = require("./agent-registration-request.js");
|
|
13
|
-
const hub_mesh_guard_js_1 = require("./hub-mesh-guard.js");
|
|
14
|
-
const cap_call_fn_js_1 = require("./cap-call-fn.js");
|
|
15
|
-
const cap_route_authority_js_1 = require("./cap-route-authority.js");
|
|
16
|
-
class MoleculerService {
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { AGENT_READINESS_SERVICE_NAME, CapRouteError, CapRouteResolver, callWithServiceDiscovery, capActionName, createBroker, createHubCapForwardService, createHubService, createLocalTransport, createParentUnownedCallHandler, createProcessService, createReadinessServiceForRegistry, createStreamProbeBrokerService, createUdsEventBridge, expandCapMethods, getBrokerTlsTransporter, getCapUsageRegistry, HubNodeRegistry, getProcessMeshCertificateRegistry, getProcessPeerLeaf, isInfraCapability, LocalChildRegistry, localEndpointPath, registerEventBusService, serializeTypedArrays, setNodeEventInterest, emitProcessNodeRegistered, setProcessMeshPeerTransportLookup, setProcessMeshTlsEnabler, subscribePassthrough, udsChildLogToWorkerEntry, UNCLAIMED, answerClaimedGetStatus, } from '@camstack/system';
|
|
3
|
+
import { EventCategory, emitReadiness, errMsg, ReadinessRegistry } from '@camstack/types';
|
|
4
|
+
import { addonPinOf, aggregateCollectionCall, COLLECTION_ARRAY_METHODS, devicePinOf, pinnedCollectionCall, pinnedDeviceCollectionCall, } from '../../api/trpc/cap-router-runtime.js';
|
|
5
|
+
import { isCoreServiceCapName } from '../../api/trpc/core-cap-bridge.js';
|
|
6
|
+
import { filterAgentAuthoritativeRecords } from './agent-readiness-pull.js';
|
|
7
|
+
import { requestAgentRegistration } from './agent-registration-request.js';
|
|
8
|
+
import { HubMeshGuard } from './hub-mesh-guard.js';
|
|
9
|
+
import { buildCapCallFn } from './cap-call-fn.js';
|
|
10
|
+
import { createHubResidentProviderLookup, createInProcessProviderLookup, createNodeCapAuthority, } from './cap-route-authority.js';
|
|
11
|
+
export class MoleculerService {
|
|
17
12
|
eventBus;
|
|
18
13
|
config;
|
|
19
14
|
logging;
|
|
20
15
|
capabilityService;
|
|
21
16
|
streamProbe;
|
|
22
17
|
broker;
|
|
23
|
-
/** Narrow-typed view of `this.broker` — see `BrokerLike` doc above. */
|
|
24
|
-
get brokerSafe() {
|
|
25
|
-
return this.broker;
|
|
26
|
-
}
|
|
27
18
|
logger;
|
|
28
19
|
/**
|
|
29
20
|
* D802, D857 — who may talk to the hub over the mesh: the broker middleware
|
|
@@ -35,7 +26,7 @@ class MoleculerService {
|
|
|
35
26
|
* D3 authority: union of every node's manifest delivered via
|
|
36
27
|
* `$hub.registerNode`. Populated by `onRegisterNode` in `hubDeps`.
|
|
37
28
|
*/
|
|
38
|
-
nodeRegistry = new
|
|
29
|
+
nodeRegistry = new HubNodeRegistry();
|
|
39
30
|
/**
|
|
40
31
|
* nodeId → the owner token of the INCARNATION whose manifest currently backs
|
|
41
32
|
* that node's capability registrations.
|
|
@@ -172,7 +163,7 @@ class MoleculerService {
|
|
|
172
163
|
/** This hub's Moleculer node id (e.g. `hub`). Hub-local forked children
|
|
173
164
|
* register under `${nodeId}/${runnerId}`. */
|
|
174
165
|
get nodeId() {
|
|
175
|
-
return this.
|
|
166
|
+
return this.broker.nodeID;
|
|
176
167
|
}
|
|
177
168
|
constructor(eventBus, config, logging, capabilityService, streamProbe) {
|
|
178
169
|
this.eventBus = eventBus;
|
|
@@ -193,8 +184,8 @@ class MoleculerService {
|
|
|
193
184
|
// `no-unsafe-assignment`. Going via `unknown` documents the boundary.
|
|
194
185
|
// No shared secret (D857): the Moleculer namespace was never authentication (TCP
|
|
195
186
|
// ignores it); a node is admitted by the certificate it presented at the TLS handshake.
|
|
196
|
-
this.meshGuard = new
|
|
197
|
-
const broker =
|
|
187
|
+
this.meshGuard = new HubMeshGuard('hub', this.logging.createLogger('mesh-auth'));
|
|
188
|
+
const broker = createBroker({
|
|
198
189
|
nodeID: 'hub',
|
|
199
190
|
mode: 'hub',
|
|
200
191
|
// D802, D857: every action and event another node sends passes the certificate gate.
|
|
@@ -206,9 +197,9 @@ class MoleculerService {
|
|
|
206
197
|
// (eventemitter2 chain unresolvable). Documented + single-site cast.
|
|
207
198
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
208
199
|
this.broker = broker;
|
|
209
|
-
this.readinessRegistry = new
|
|
200
|
+
this.readinessRegistry = new ReadinessRegistry({
|
|
210
201
|
eventBus: this.eventBus,
|
|
211
|
-
sourceNodeId: this.
|
|
202
|
+
sourceNodeId: this.broker.nodeID,
|
|
212
203
|
logger: this.logging.createLogger('readiness'),
|
|
213
204
|
});
|
|
214
205
|
}
|
|
@@ -251,19 +242,20 @@ class MoleculerService {
|
|
|
251
242
|
});
|
|
252
243
|
},
|
|
253
244
|
onSetLogLevel: (level) => {
|
|
254
|
-
const factory = this.
|
|
255
|
-
const
|
|
245
|
+
const factory = this.broker.logger;
|
|
246
|
+
const moleculerLogFactory = factory;
|
|
247
|
+
const appenders = moleculerLogFactory.appenders;
|
|
256
248
|
if (appenders) {
|
|
257
249
|
for (const appender of appenders) {
|
|
258
250
|
appender.opts.level = level;
|
|
259
251
|
}
|
|
260
252
|
}
|
|
261
|
-
const cache =
|
|
253
|
+
const cache = moleculerLogFactory.cache;
|
|
262
254
|
if (cache) {
|
|
263
255
|
cache.clear();
|
|
264
256
|
}
|
|
265
257
|
logger.info('Moleculer log level changed', { meta: { level } });
|
|
266
|
-
this.
|
|
258
|
+
this.broker.call('$process.setLogLevel', { level }).catch(() => { });
|
|
267
259
|
},
|
|
268
260
|
// D3: registration-handshake path. Nodes send $hub.registerNode with
|
|
269
261
|
// their complete capability manifest; the hub applies it immediately.
|
|
@@ -276,8 +268,8 @@ class MoleculerService {
|
|
|
276
268
|
// D857: a node registers only itself, and only with a certificate identity.
|
|
277
269
|
meshIdentity: this.meshGuard.gate,
|
|
278
270
|
};
|
|
279
|
-
const hubService =
|
|
280
|
-
this.
|
|
271
|
+
const hubService = createHubService(hubDeps);
|
|
272
|
+
this.broker.createService(hubService);
|
|
281
273
|
// Forked addon-runner children must root their storage (addons-data,
|
|
282
274
|
// the sqlite settings DB) under the SAME data path as the hub's main
|
|
283
275
|
// process. The configured key is `server.dataPath` (populated from the
|
|
@@ -293,7 +285,7 @@ class MoleculerService {
|
|
|
293
285
|
// broker-only — no parentUdsPath is propagated.
|
|
294
286
|
let parentUdsPath;
|
|
295
287
|
try {
|
|
296
|
-
const nodeId = this.
|
|
288
|
+
const nodeId = this.broker.nodeID;
|
|
297
289
|
// F0 (slice-5 outbound): when a forked child issues `ctx.api.<cap>` for a
|
|
298
290
|
// cap NO local sibling owns, route it from the PARENT and return the
|
|
299
291
|
// result over UDS — resolver-first, broker-fallback. Closes over `this`
|
|
@@ -304,7 +296,7 @@ class MoleculerService {
|
|
|
304
296
|
// capabilities the resolver can see. Before F0 this fell through
|
|
305
297
|
// UDS_NO_ROUTE → the child's own brokerTransportLink; F1+F2 removes that
|
|
306
298
|
// child broker, so the parent must own this path.
|
|
307
|
-
const onUnownedCall =
|
|
299
|
+
const onUnownedCall = createParentUnownedCallHandler({
|
|
308
300
|
getResolver: () => this.resolver,
|
|
309
301
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- moleculer types unresolvable; see `broker` getter docstring
|
|
310
302
|
broker: this.broker,
|
|
@@ -362,7 +354,7 @@ class MoleculerService {
|
|
|
362
354
|
return null;
|
|
363
355
|
if (reg.getDefinition(capName)?.mode !== 'collection')
|
|
364
356
|
return null;
|
|
365
|
-
if (!
|
|
357
|
+
if (!COLLECTION_ARRAY_METHODS.get(capName)?.includes(method))
|
|
366
358
|
return null;
|
|
367
359
|
if (reg.getCollectionEntries(capName).length > 0)
|
|
368
360
|
return null;
|
|
@@ -376,7 +368,7 @@ class MoleculerService {
|
|
|
376
368
|
// never disagree again). Routing alone picks ONE provider, which is
|
|
377
369
|
// how the Notification Center saw an empty `notification-output`
|
|
378
370
|
// catalog and dead-lettered every notification (2026-07-30).
|
|
379
|
-
callCollectionAggregate: (capName, method, args) =>
|
|
371
|
+
callCollectionAggregate: (capName, method, args) => aggregateCollectionCall(this.capabilityService.getRegistry(), capName, method, args),
|
|
380
372
|
// `{ addonId }` PIN for the same child→parent path — the scalar twin of
|
|
381
373
|
// the fan-out above, sharing this server's provider resolution so the
|
|
382
374
|
// two paths cannot disagree. Routing takes only `nodeId`/`deviceId`, so
|
|
@@ -391,7 +383,7 @@ class MoleculerService {
|
|
|
391
383
|
// addon's clip byte read named `provider-reolink` and was answered by
|
|
392
384
|
// whichever child registered first — the recorder, then export-hap
|
|
393
385
|
// after a redeploy in a different order (2026-09-22, D594).
|
|
394
|
-
callPinnedCollection: async (capName, method, args) => (await
|
|
386
|
+
callPinnedCollection: async (capName, method, args) => (await pinnedDeviceCollectionCall(this.capabilityService.getRegistry(), capName, method, args)) ?? pinnedCollectionCall(this.capabilityService.getRegistry(), capName, method, args),
|
|
395
387
|
// Hub-core `$`-service discriminant for the general
|
|
396
388
|
// provider-not-yet-registered recovery. A resolver miss on a NON-core
|
|
397
389
|
// cap (any addon-provided system/singleton cap, e.g.
|
|
@@ -401,7 +393,7 @@ class MoleculerService {
|
|
|
401
393
|
// predicate lets it retry the resolver + throw a precise error instead.
|
|
402
394
|
// Derived from the already-maintained core-router list, so a new
|
|
403
395
|
// multi-node addon cap is covered with no change here.
|
|
404
|
-
isCoreServiceCap: (capName) =>
|
|
396
|
+
isCoreServiceCap: (capName) => isCoreServiceCapName(capName),
|
|
405
397
|
logger: {
|
|
406
398
|
debug: (msg, meta) => logger.debug(msg, meta !== null && meta !== undefined
|
|
407
399
|
? { meta: meta }
|
|
@@ -419,7 +411,7 @@ class MoleculerService {
|
|
|
419
411
|
// `broker.call('$hub-cap-fwd.forward', …)` discovers it here. Without it a
|
|
420
412
|
// remote agent's raw `${cap}.*` discovery 30s-deadlines (hub-local addon
|
|
421
413
|
// runners expose no Moleculer service). See hub-cap-forward.ts.
|
|
422
|
-
this.
|
|
414
|
+
this.broker.createService(createHubCapForwardService(onUnownedCall, {
|
|
423
415
|
// The agent stamps WHO is calling (its child's runner id) into the
|
|
424
416
|
// envelope (D787). Believe it only when that runner is on the agent
|
|
425
417
|
// the request came from — a node's registered manifest lists the
|
|
@@ -429,8 +421,8 @@ class MoleculerService {
|
|
|
429
421
|
meta: { senderNodeId, callerRunnerId },
|
|
430
422
|
}),
|
|
431
423
|
}));
|
|
432
|
-
const registry = new
|
|
433
|
-
server:
|
|
424
|
+
const registry = new LocalChildRegistry({
|
|
425
|
+
server: createLocalTransport().createServer(nodeId),
|
|
434
426
|
// This node's own id — lets the cap-call-out router serve a pin-to-self
|
|
435
427
|
// from the co-resident sibling instead of bouncing it through
|
|
436
428
|
// onUnownedCall (a pin to ANOTHER node still bypasses the sibling).
|
|
@@ -456,7 +448,7 @@ class MoleculerService {
|
|
|
456
448
|
return false;
|
|
457
449
|
if (reg.getDefinition(capName)?.mode !== 'collection')
|
|
458
450
|
return false;
|
|
459
|
-
return
|
|
451
|
+
return COLLECTION_ARRAY_METHODS.get(capName)?.includes(method) === true;
|
|
460
452
|
},
|
|
461
453
|
// Same story for an explicit `{ addonId }` selector: the sibling
|
|
462
454
|
// short-circuit picks the FIRST-registered child, which is precisely
|
|
@@ -472,8 +464,8 @@ class MoleculerService {
|
|
|
472
464
|
// routing that would honour it — "honoured only there is honoured
|
|
473
465
|
// never", which is how the first attempt at the `addonId` fix failed
|
|
474
466
|
// live, and how the `provider` pin failed twice more (D592, D593).
|
|
475
|
-
isAddonPinnedCall: (capName, method, args) =>
|
|
476
|
-
|
|
467
|
+
isAddonPinnedCall: (capName, method, args) => addonPinOf(this.capabilityService.getRegistry(), capName, method, args) !== null ||
|
|
468
|
+
devicePinOf(this.capabilityService.getRegistry(), capName, args) !== null,
|
|
477
469
|
// Bound every UDS cap call by the cap's own declared timeout, falling
|
|
478
470
|
// back to the channel default. Same resolver the REMOTE plane uses
|
|
479
471
|
// below, so a method that survives 16 minutes across nodes survives it
|
|
@@ -487,7 +479,7 @@ class MoleculerService {
|
|
|
487
479
|
// RUNNER's own module singleton, which hub-main never sees — which is
|
|
488
480
|
// why the graph answered `[]` on a hub running 64 addons. Blind spots
|
|
489
481
|
// that remain are listed on `LocalChildRegistryOptions.capUsageObserver`.
|
|
490
|
-
capUsageObserver: (obs) =>
|
|
482
|
+
capUsageObserver: (obs) => getCapUsageRegistry().recordCall(obs),
|
|
491
483
|
logger: {
|
|
492
484
|
info: (msg, meta) => logger.info(msg, meta !== null && meta !== undefined
|
|
493
485
|
? { meta: meta }
|
|
@@ -516,7 +508,7 @@ class MoleculerService {
|
|
|
516
508
|
// Phase F removes the child broker; `onRegisterNode`'s diff logic ensures
|
|
517
509
|
// double-apply is idempotent (same nodeId + same caps → no-op on the second call).
|
|
518
510
|
registry.onChildRegistered((child) => {
|
|
519
|
-
const hubNodeId = this.
|
|
511
|
+
const hubNodeId = this.broker.nodeID;
|
|
520
512
|
const childNodeId = `${hubNodeId}/${child.childId}`;
|
|
521
513
|
// The catalogs first, and unconditionally: the manifest gate below
|
|
522
514
|
// judges CAPS, and a frame whose caps are unchanged may still be the
|
|
@@ -528,7 +520,7 @@ class MoleculerService {
|
|
|
528
520
|
}
|
|
529
521
|
catch (err) {
|
|
530
522
|
logger.warn('UDS child custom-action catalogs could not be applied', {
|
|
531
|
-
meta: { nodeId: childNodeId, incarnation: child.incarnation, error:
|
|
523
|
+
meta: { nodeId: childNodeId, incarnation: child.incarnation, error: errMsg(err) },
|
|
532
524
|
});
|
|
533
525
|
}
|
|
534
526
|
}
|
|
@@ -551,7 +543,7 @@ class MoleculerService {
|
|
|
551
543
|
nodeId: childNodeId,
|
|
552
544
|
incarnation: child.incarnation,
|
|
553
545
|
skipped: this.childManifestGate.skippedSince(),
|
|
554
|
-
sampleEvery:
|
|
546
|
+
sampleEvery: CHILD_MANIFEST_SKIP_SAMPLE,
|
|
555
547
|
},
|
|
556
548
|
});
|
|
557
549
|
}
|
|
@@ -568,7 +560,7 @@ class MoleculerService {
|
|
|
568
560
|
// E1: cleanup on child disconnect — same effect as `$node.disconnected`
|
|
569
561
|
// for hub-local children. The Moleculer path stays for AGENT nodes.
|
|
570
562
|
registry.onChildGone((childId, incarnation) => {
|
|
571
|
-
const hubNodeId = this.
|
|
563
|
+
const hubNodeId = this.broker.nodeID;
|
|
572
564
|
const childNodeId = `${hubNodeId}/${childId}`;
|
|
573
565
|
// The successor's first register must be a FIRST register: leaving the
|
|
574
566
|
// record behind would let a same-incarnation reconnect (mixed-version
|
|
@@ -585,7 +577,7 @@ class MoleculerService {
|
|
|
585
577
|
// This runs in PARALLEL with the existing $hub.log / onLog broker path —
|
|
586
578
|
// both stay active until Phase F removes the broker path.
|
|
587
579
|
registry.onChildLog((childId, entry) => {
|
|
588
|
-
this.logging.writeFromWorker(
|
|
580
|
+
this.logging.writeFromWorker(udsChildLogToWorkerEntry(childId, entry));
|
|
589
581
|
});
|
|
590
582
|
// D1: answer readiness-snapshot requests from UDS children so they can
|
|
591
583
|
// hydrate without a `$readiness.getSnapshot` Moleculer call.
|
|
@@ -596,7 +588,7 @@ class MoleculerService {
|
|
|
596
588
|
// Keep `$readiness.getSnapshot` intact — Phase F removes it.
|
|
597
589
|
registry.onReadinessSnapshotRequest(() => this.readinessRegistry.getSnapshotForTransport());
|
|
598
590
|
this.localChildRegistry = registry;
|
|
599
|
-
parentUdsPath =
|
|
591
|
+
parentUdsPath = localEndpointPath(nodeId);
|
|
600
592
|
logger.info('UDS child registry listening', { meta: { path: parentUdsPath } });
|
|
601
593
|
}
|
|
602
594
|
catch (err) {
|
|
@@ -604,15 +596,14 @@ class MoleculerService {
|
|
|
604
596
|
meta: { err: err instanceof Error ? err.message : String(err) },
|
|
605
597
|
});
|
|
606
598
|
}
|
|
607
|
-
const processService =
|
|
608
|
-
this.
|
|
599
|
+
const processService = createProcessService(this.broker.nodeID, dataDir, undefined, undefined, parentUdsPath);
|
|
600
|
+
this.broker.createService(processService);
|
|
609
601
|
// $addonHost — REMOVED (Sprint 6). Three-level settings are now
|
|
610
602
|
// served by the `addon-settings` singleton capability. Per-addon
|
|
611
603
|
// Moleculer services expose `settings.*` actions for remote agents.
|
|
612
604
|
// D3: mirror $node.disconnected onto the registry path so nodes that
|
|
613
605
|
// sent a $hub.registerNode manifest get cleaned up on disconnect.
|
|
614
|
-
|
|
615
|
-
bridgeBus.localBus.on('$node.disconnected', ({ node }) => {
|
|
606
|
+
this.broker.localBus.on('$node.disconnected', ({ node }) => {
|
|
616
607
|
// No owner token: this listener cannot observe WHICH incarnation died.
|
|
617
608
|
// For a hub-local child the UDS `onChildGone` is the authority (it holds
|
|
618
609
|
// the incarnation) and this call is refused if the id has been reclaimed;
|
|
@@ -620,7 +611,7 @@ class MoleculerService {
|
|
|
620
611
|
this.removeNodeFromRegistry(node.id);
|
|
621
612
|
this.meshGuard.forget(node.id);
|
|
622
613
|
// Release A: its next connection decides again whether it is `tls` or `clear`.
|
|
623
|
-
|
|
614
|
+
getBrokerTlsTransporter(this.broker)?.forgetPeer(node.id);
|
|
624
615
|
});
|
|
625
616
|
// D8 reverse-leg snapshot-reconcile: on every agent (re)connect, pull
|
|
626
617
|
// the agent's own readiness snapshot (`$agent-readiness.getSnapshot`)
|
|
@@ -630,16 +621,16 @@ class MoleculerService {
|
|
|
630
621
|
// this registry stuck on the synthesized `down` forever (2026-07-17:
|
|
631
622
|
// pipeline-executor@little-unraid, 3.5h dispatch outage). The
|
|
632
623
|
// periodic sweep below repairs deltas lost while CONNECTED.
|
|
633
|
-
|
|
624
|
+
this.broker.localBus.on('$node.connected', ({ node }) => {
|
|
634
625
|
const nodeId = node.id;
|
|
635
|
-
if (nodeId === this.
|
|
626
|
+
if (nodeId === this.broker.nodeID || nodeId.includes('/'))
|
|
636
627
|
return;
|
|
637
628
|
void this.pullAgentReadinessSnapshot(nodeId);
|
|
638
629
|
// The hub may have dropped this agent's registration on a disconnect
|
|
639
630
|
// only the hub saw (gossip, a failed write): ask it for its full set
|
|
640
631
|
// (`agent-registration-request.ts`, 2026-10-14).
|
|
641
|
-
void
|
|
642
|
-
call: (action, params, opts) => this.
|
|
632
|
+
void requestAgentRegistration({
|
|
633
|
+
call: (action, params, opts) => this.broker.call(action, params, opts),
|
|
643
634
|
isRegistered: (id) => this.isNodeRegistered(id),
|
|
644
635
|
warn: (message, id, meta) => this.logger.warn(message, { tags: { nodeId: id }, meta }),
|
|
645
636
|
info: (message, id, meta) => this.logger.info(message, { tags: { nodeId: id }, meta }),
|
|
@@ -647,7 +638,7 @@ class MoleculerService {
|
|
|
647
638
|
});
|
|
648
639
|
this.agentReadinessPullTimer = setInterval(() => {
|
|
649
640
|
for (const nodeId of this.nodeRegistry.listNodeIds()) {
|
|
650
|
-
if (nodeId === this.
|
|
641
|
+
if (nodeId === this.broker.nodeID || nodeId.includes('/'))
|
|
651
642
|
continue;
|
|
652
643
|
void this.pullAgentReadinessSnapshot(nodeId);
|
|
653
644
|
}
|
|
@@ -660,26 +651,26 @@ class MoleculerService {
|
|
|
660
651
|
// handshake, not dynamically after post-start `createService()`.
|
|
661
652
|
// Without this, cross-node broadcasts (camstack.evt.<category>) would
|
|
662
653
|
// arrive unreliably for the first ~10s after each node joins.
|
|
663
|
-
|
|
654
|
+
registerEventBusService(this.broker);
|
|
664
655
|
// Register the hub-authoritative `$readiness.getSnapshot` service
|
|
665
656
|
// BEFORE broker.start() so workers / agents see it in their initial
|
|
666
657
|
// INFO packet — post-start `createService` calls propagate via
|
|
667
658
|
// heartbeat (several seconds) and would force workers to poll.
|
|
668
|
-
this.
|
|
659
|
+
this.broker.createService(createReadinessServiceForRegistry(this.readinessRegistry));
|
|
669
660
|
// Register the hub-authoritative `$stream-probe` service —
|
|
670
661
|
// workers route RTSP probe + field-probe through this action when
|
|
671
662
|
// the tRPC WSS link isn't available (default path for forked
|
|
672
663
|
// workers). Keeps ffprobe + HTTP-reachability as a single
|
|
673
664
|
// hub-side implementation; see `createStreamProbeBrokerService`
|
|
674
665
|
// for the shape.
|
|
675
|
-
this.
|
|
666
|
+
this.broker.createService(createStreamProbeBrokerService({
|
|
676
667
|
probe: (url, options) => this.streamProbe.probe(url, options),
|
|
677
668
|
probeField: (key, value) => this.streamProbe.probeField(key, value),
|
|
678
669
|
}));
|
|
679
670
|
// D826: when system-config's MeshCaKeeper becomes ready it offers hub leaf
|
|
680
671
|
// material here; the transporter listens on mutual TLS only (:6443).
|
|
681
|
-
|
|
682
|
-
const tx =
|
|
672
|
+
setProcessMeshTlsEnabler(async (material) => {
|
|
673
|
+
const tx = getBrokerTlsTransporter(this.broker);
|
|
683
674
|
if (tx === null) {
|
|
684
675
|
logger.warn('Mesh TLS material ready but broker transporter is not TlsTcpTransporter');
|
|
685
676
|
return;
|
|
@@ -687,13 +678,13 @@ class MoleculerService {
|
|
|
687
678
|
await tx.enableMeshTls(material);
|
|
688
679
|
logger.info('Mesh mTLS listener enabled (TLS-only hub mesh)');
|
|
689
680
|
});
|
|
690
|
-
|
|
691
|
-
transportOf: (nodeId) =>
|
|
692
|
-
isVerifiedPeer: (nodeId) =>
|
|
693
|
-
peerLeaf: (nodeId) =>
|
|
694
|
-
dropPeer: (nodeId) =>
|
|
681
|
+
setProcessMeshPeerTransportLookup({
|
|
682
|
+
transportOf: (nodeId) => getBrokerTlsTransporter(this.broker)?.peerTransport(nodeId),
|
|
683
|
+
isVerifiedPeer: (nodeId) => getBrokerTlsTransporter(this.broker)?.verifiedPeer(nodeId) !== undefined,
|
|
684
|
+
peerLeaf: (nodeId) => getBrokerTlsTransporter(this.broker)?.peerLeaf(nodeId),
|
|
685
|
+
dropPeer: (nodeId) => getBrokerTlsTransporter(this.broker)?.dropPeer(nodeId) ?? 0,
|
|
695
686
|
});
|
|
696
|
-
await this.
|
|
687
|
+
await this.broker.start();
|
|
697
688
|
logger.info('Moleculer broker started (TCP transport)');
|
|
698
689
|
// Construct the CapRouteResolver now that both the broker and
|
|
699
690
|
// localChildRegistry are ready. The resolver reads live registry state
|
|
@@ -701,21 +692,21 @@ class MoleculerService {
|
|
|
701
692
|
// children connecting/disconnecting after this point are picked up
|
|
702
693
|
// correctly. The localChildRegistry reference is captured and may be
|
|
703
694
|
// null if UDS failed to start.
|
|
704
|
-
this.resolver = new
|
|
705
|
-
hubNodeId: this.
|
|
706
|
-
broker: this.
|
|
695
|
+
this.resolver = new CapRouteResolver({
|
|
696
|
+
hubNodeId: this.broker.nodeID,
|
|
697
|
+
broker: this.broker,
|
|
707
698
|
hubLocalRegistry: this.localChildRegistry,
|
|
708
|
-
nodeAuthority:
|
|
699
|
+
nodeAuthority: createNodeCapAuthority(this.nodeRegistry, {
|
|
709
700
|
resolveSingleton: (capName, nodeId) => this.capabilityService.getRegistry()?.resolveSingletonAddonIdForNode(capName, nodeId) ??
|
|
710
701
|
null,
|
|
711
702
|
}),
|
|
712
|
-
inProcessProviders:
|
|
703
|
+
inProcessProviders: createInProcessProviderLookup(this.capabilityService),
|
|
713
704
|
// Node-accurate lookup for EXPLICITLY hub-pinned calls: unlike the
|
|
714
705
|
// lookup above (which falls back to the cluster-elected ACTIVE
|
|
715
706
|
// singleton — possibly a remote proxy), this resolves only providers
|
|
716
707
|
// physically resident on the hub, so a `nodePin('hub')` fails precisely
|
|
717
708
|
// instead of silently executing on another node.
|
|
718
|
-
hubResidentProviders:
|
|
709
|
+
hubResidentProviders: createHubResidentProviderLookup(this.capabilityService),
|
|
719
710
|
// Per-cap-method RPC timeout override (e.g. model-convert.convert, which
|
|
720
711
|
// runs for minutes) — read from the cap definition's declared timeoutMs.
|
|
721
712
|
capTimeoutMs: (capName, method) => this.capabilityService.getRegistry()?.getDefinition(capName)?.methods?.[method]?.timeoutMs,
|
|
@@ -736,15 +727,15 @@ class MoleculerService {
|
|
|
736
727
|
// is wired after attachBroker so the parentBus is backed by the real
|
|
737
728
|
// shared broker bus and broker.broadcast is live.
|
|
738
729
|
if (this.localChildRegistry !== null) {
|
|
739
|
-
const hubNodeId = this.
|
|
740
|
-
this.udsEventBridgeDispose =
|
|
730
|
+
const hubNodeId = this.broker.nodeID;
|
|
731
|
+
this.udsEventBridgeDispose = createUdsEventBridge({
|
|
741
732
|
registry: this.localChildRegistry,
|
|
742
733
|
parentBus: this.eventBus,
|
|
743
734
|
parentNodeId: hubNodeId,
|
|
744
735
|
// D2: relay via pass-through so the bridge's wildcard is not counted as
|
|
745
736
|
// local interest (uniform with the agent; harmless here since the hub
|
|
746
737
|
// retains and always accepts inbound cross-node events anyway).
|
|
747
|
-
subscribePassthrough: (handler) =>
|
|
738
|
+
subscribePassthrough: (handler) => subscribePassthrough(this.broker, handler),
|
|
748
739
|
});
|
|
749
740
|
// D2: expose the hub's aggregate child interest to its `$event-bus`
|
|
750
741
|
// inbound gate for uniformity with the agent. NOTE: the hub main process
|
|
@@ -754,7 +745,7 @@ class MoleculerService {
|
|
|
754
745
|
// wiring is therefore harmless-but-uniform; the real efficiency win is on
|
|
755
746
|
// agents (non-retaining nodes).
|
|
756
747
|
const hubChildRegistry = this.localChildRegistry;
|
|
757
|
-
|
|
748
|
+
setNodeEventInterest(this.broker, () => hubChildRegistry.aggregateEventInterest());
|
|
758
749
|
}
|
|
759
750
|
}
|
|
760
751
|
/**
|
|
@@ -763,7 +754,7 @@ class MoleculerService {
|
|
|
763
754
|
* the updated service list to the network.
|
|
764
755
|
*/
|
|
765
756
|
registerLogReceiver() {
|
|
766
|
-
this.
|
|
757
|
+
this.broker.createService({
|
|
767
758
|
name: 'log-receiver',
|
|
768
759
|
actions: {
|
|
769
760
|
ingest: {
|
|
@@ -795,7 +786,7 @@ class MoleculerService {
|
|
|
795
786
|
* `registerLogReceiver` relies on the same post-init registration.
|
|
796
787
|
*/
|
|
797
788
|
registerCoreCapService(service) {
|
|
798
|
-
this.
|
|
789
|
+
this.broker.createService(service);
|
|
799
790
|
}
|
|
800
791
|
/**
|
|
801
792
|
* The hub's claim authority (D663, S3): `CapabilityRegistry.resolveClaimedStatus`,
|
|
@@ -806,7 +797,7 @@ class MoleculerService {
|
|
|
806
797
|
const registry = this.capabilityService.getRegistry();
|
|
807
798
|
return registry !== null
|
|
808
799
|
? registry.resolveClaimedStatus(capName, deviceId)
|
|
809
|
-
: Promise.resolve(
|
|
800
|
+
: Promise.resolve(UNCLAIMED);
|
|
810
801
|
}
|
|
811
802
|
/**
|
|
812
803
|
* Call a capability method on a specific node.
|
|
@@ -831,7 +822,7 @@ class MoleculerService {
|
|
|
831
822
|
// a `nodeId` pin — the generated DeviceProxy always sends one — and every
|
|
832
823
|
// hub in-process addon's `ctx.api` through `deviceCapResolverLink`); a pin
|
|
833
824
|
// does not bypass the claim authority. Same shared function as the other two.
|
|
834
|
-
const claimed = await
|
|
825
|
+
const claimed = await answerClaimedGetStatus((capName, deviceId) => this.resolveClaimedStatus(capName, deviceId), { capName: capabilityName, method: methodName, args: params });
|
|
835
826
|
if (claimed !== null)
|
|
836
827
|
return claimed.slice;
|
|
837
828
|
const resolver = this.resolver;
|
|
@@ -846,7 +837,7 @@ class MoleculerService {
|
|
|
846
837
|
return await resolver.dispatch(route, methodName, params);
|
|
847
838
|
}
|
|
848
839
|
catch (err) {
|
|
849
|
-
if (err instanceof
|
|
840
|
+
if (err instanceof CapRouteError &&
|
|
850
841
|
(err.reason === 'no-provider' || err.reason === 'node-offline')) {
|
|
851
842
|
// Resolver couldn't find the cap — try the legacy callFn store as a
|
|
852
843
|
// fallback. This covers caps registered in nodeCallFns (e.g. agent
|
|
@@ -867,7 +858,7 @@ class MoleculerService {
|
|
|
867
858
|
// Pre-init fallback (resolver not yet constructed — before onModuleInit).
|
|
868
859
|
// This path is only reachable in tests that drive onRegisterNode without
|
|
869
860
|
// calling onModuleInit first.
|
|
870
|
-
if (nodeId === 'hub' || nodeId === this.
|
|
861
|
+
if (nodeId === 'hub' || nodeId === this.broker.nodeID) {
|
|
871
862
|
const registry = this.capabilityService.getRegistry();
|
|
872
863
|
const provider = registry?.getSingleton(capabilityName) ?? null;
|
|
873
864
|
if (provider !== null) {
|
|
@@ -881,7 +872,7 @@ class MoleculerService {
|
|
|
881
872
|
if (callFn) {
|
|
882
873
|
return callFn(methodName, params);
|
|
883
874
|
}
|
|
884
|
-
throw new
|
|
875
|
+
throw new CapRouteError(capabilityName, methodName, {
|
|
885
876
|
reason: 'no-provider',
|
|
886
877
|
nodeId,
|
|
887
878
|
rejected: [
|
|
@@ -912,7 +903,7 @@ class MoleculerService {
|
|
|
912
903
|
this.observePresentedMeshLeaf(params.nodeId);
|
|
913
904
|
// The node's capabilities are routable now: whoever waits for this registration (a mesh
|
|
914
905
|
// leaf renewal confirming the node came back WITH the new leaf) is told, with no polling.
|
|
915
|
-
|
|
906
|
+
emitProcessNodeRegistered(params.nodeId, params.addons.map((addon) => addon.addonId));
|
|
916
907
|
// Notify AgentRegistryService to reconcile placement for bare-ID
|
|
917
908
|
// agent nodes (no '/' = not a hub child worker, not the hub itself).
|
|
918
909
|
// The handshake is the authoritative completeness signal — the full
|
|
@@ -946,7 +937,7 @@ class MoleculerService {
|
|
|
946
937
|
*/
|
|
947
938
|
applyNodeManifest(params, previousManifest, owner, previousOwner) {
|
|
948
939
|
const { nodeId, addons } = params;
|
|
949
|
-
const hubNodeId = this.
|
|
940
|
+
const hubNodeId = this.broker.nodeID;
|
|
950
941
|
const isLocalChild = nodeId.startsWith(hubNodeId + '/');
|
|
951
942
|
const isHubInProcess = nodeId === hubNodeId;
|
|
952
943
|
// Hub in-process addons register themselves during initialize() — skip.
|
|
@@ -975,7 +966,7 @@ class MoleculerService {
|
|
|
975
966
|
// isolate (D231) IS this node's engine, and skipping it leaves
|
|
976
967
|
// `getCollection` empty. Hub-local children are proxied; remote nodes
|
|
977
968
|
// are not (D233).
|
|
978
|
-
const skipInfraFromRemote = (capName) =>
|
|
969
|
+
const skipInfraFromRemote = (capName) => isInfraCapability(capName) && !isLocalChild;
|
|
979
970
|
const appliedKeys = (manifest) => {
|
|
980
971
|
const keys = new Map();
|
|
981
972
|
for (const addon of manifest) {
|
|
@@ -1041,14 +1032,14 @@ class MoleculerService {
|
|
|
1041
1032
|
// UDS-migration gap where this dispatcher hand-rolled a `broker.call`
|
|
1042
1033
|
// to an agent that exposes no Moleculer service for the cap.
|
|
1043
1034
|
// - resolver not yet built (pre-init) → legacy Moleculer call.
|
|
1044
|
-
const callFn =
|
|
1035
|
+
const callFn = buildCapCallFn({
|
|
1045
1036
|
capName,
|
|
1046
1037
|
addonId,
|
|
1047
1038
|
nodeId,
|
|
1048
1039
|
udsChildId,
|
|
1049
1040
|
getLocalChildRegistry: () => this.localChildRegistry,
|
|
1050
1041
|
getResolver: () => this.resolver,
|
|
1051
|
-
legacyBrokerCall: (method, methodParams, targetNode) =>
|
|
1042
|
+
legacyBrokerCall: (method, methodParams, targetNode) => callWithServiceDiscovery(this.broker, addonId, capActionName(addonId, capName, method, false), serializeTypedArrays(methodParams), { nodeID: targetNode, timeout: 60_000 }),
|
|
1052
1043
|
onUdsRoute: (cap) => {
|
|
1053
1044
|
if (!this.udsRoutedCaps.has(cap)) {
|
|
1054
1045
|
this.udsRoutedCaps.add(cap);
|
|
@@ -1057,7 +1048,7 @@ class MoleculerService {
|
|
|
1057
1048
|
},
|
|
1058
1049
|
});
|
|
1059
1050
|
const proxy = { id: addonId, nodeId };
|
|
1060
|
-
for (const methodName of Object.keys(
|
|
1051
|
+
for (const methodName of Object.keys(expandCapMethods(capDef))) {
|
|
1061
1052
|
proxy[methodName] = (methodParams) => callFn(methodName, methodParams);
|
|
1062
1053
|
}
|
|
1063
1054
|
registry.registerProvider(capName, registryKey, proxy, owner);
|
|
@@ -1081,19 +1072,19 @@ class MoleculerService {
|
|
|
1081
1072
|
// refreshes its page/widget registry for cross-process addons.
|
|
1082
1073
|
if (capName === 'addon-pages-source') {
|
|
1083
1074
|
this.eventBus.emit({
|
|
1084
|
-
id:
|
|
1075
|
+
id: randomUUID(),
|
|
1085
1076
|
timestamp: new Date(),
|
|
1086
1077
|
source: { type: 'addon', id: addonId },
|
|
1087
|
-
category:
|
|
1078
|
+
category: EventCategory.AddonPageReady,
|
|
1088
1079
|
data: { addonId, nodeId },
|
|
1089
1080
|
});
|
|
1090
1081
|
}
|
|
1091
1082
|
if (capName === 'addon-widgets-source') {
|
|
1092
1083
|
this.eventBus.emit({
|
|
1093
|
-
id:
|
|
1084
|
+
id: randomUUID(),
|
|
1094
1085
|
timestamp: new Date(),
|
|
1095
1086
|
source: { type: 'addon', id: addonId },
|
|
1096
|
-
category:
|
|
1087
|
+
category: EventCategory.AddonWidgetReady,
|
|
1097
1088
|
data: { addonId, nodeId },
|
|
1098
1089
|
});
|
|
1099
1090
|
}
|
|
@@ -1121,9 +1112,9 @@ class MoleculerService {
|
|
|
1121
1112
|
const manifest = this.nodeRegistry.getNodeManifest(nodeId);
|
|
1122
1113
|
if (!manifest)
|
|
1123
1114
|
return; // node never sent a handshake — nothing to do
|
|
1124
|
-
const hubNodeId = this.
|
|
1115
|
+
const hubNodeId = this.broker.nodeID;
|
|
1125
1116
|
const isLocalChild = nodeId.startsWith(hubNodeId + '/');
|
|
1126
|
-
const disconnectGen = `disconnect-${nodeId}-${
|
|
1117
|
+
const disconnectGen = `disconnect-${nodeId}-${randomUUID()}`;
|
|
1127
1118
|
const agentNodeId = nodeId.includes('/') ? nodeId.split('/')[0] : nodeId;
|
|
1128
1119
|
const registry = this.capabilityService.getRegistry();
|
|
1129
1120
|
for (const addon of manifest) {
|
|
@@ -1137,7 +1128,7 @@ class MoleculerService {
|
|
|
1137
1128
|
for (const capName of capabilities) {
|
|
1138
1129
|
this.nodeCallFns.delete(`${nodeId}::${capName}`);
|
|
1139
1130
|
try {
|
|
1140
|
-
|
|
1131
|
+
emitReadiness(this.eventBus, {
|
|
1141
1132
|
capName,
|
|
1142
1133
|
scope: { type: 'node', nodeId: agentNodeId },
|
|
1143
1134
|
state: 'down',
|
|
@@ -1194,7 +1185,7 @@ class MoleculerService {
|
|
|
1194
1185
|
isReachableViaLegacy(nodeId, capabilityName) {
|
|
1195
1186
|
if (this.findCallFn(nodeId, capabilityName) !== undefined)
|
|
1196
1187
|
return true;
|
|
1197
|
-
return this.localChildRegistry !== null && nodeId.startsWith(`${this.
|
|
1188
|
+
return this.localChildRegistry !== null && nodeId.startsWith(`${this.broker.nodeID}/`);
|
|
1198
1189
|
}
|
|
1199
1190
|
/**
|
|
1200
1191
|
* Build a proxy object that forwards every method call on a capability
|
|
@@ -1223,7 +1214,7 @@ class MoleculerService {
|
|
|
1223
1214
|
// Resolver found a route — proxy is reachable.
|
|
1224
1215
|
}
|
|
1225
1216
|
catch (err) {
|
|
1226
|
-
if (err instanceof
|
|
1217
|
+
if (err instanceof CapRouteError &&
|
|
1227
1218
|
(err.reason === 'no-provider' || err.reason === 'node-offline')) {
|
|
1228
1219
|
// Check legacy callFn store and hub-local child fallbacks.
|
|
1229
1220
|
if (!this.isReachableViaLegacy(nodeId, capabilityName))
|
|
@@ -1285,8 +1276,8 @@ class MoleculerService {
|
|
|
1285
1276
|
* registry that is not up, or a write that fails, must not fail the node's registration.
|
|
1286
1277
|
*/
|
|
1287
1278
|
observePresentedMeshLeaf(nodeId) {
|
|
1288
|
-
const registry =
|
|
1289
|
-
const leaf =
|
|
1279
|
+
const registry = getProcessMeshCertificateRegistry();
|
|
1280
|
+
const leaf = getProcessPeerLeaf(nodeId);
|
|
1290
1281
|
if (registry === null || leaf === undefined)
|
|
1291
1282
|
return;
|
|
1292
1283
|
registry
|
|
@@ -1329,7 +1320,7 @@ class MoleculerService {
|
|
|
1329
1320
|
* to the Moleculer `$node-mgmt.setLogLevel` action.
|
|
1330
1321
|
*/
|
|
1331
1322
|
setChildLogLevelByNodeId(nodeId, level) {
|
|
1332
|
-
const hubNodeId = this.
|
|
1323
|
+
const hubNodeId = this.broker.nodeID;
|
|
1333
1324
|
if (!nodeId.startsWith(`${hubNodeId}/`))
|
|
1334
1325
|
return false;
|
|
1335
1326
|
const childId = nodeId.slice(hubNodeId.length + 1);
|
|
@@ -1348,10 +1339,10 @@ class MoleculerService {
|
|
|
1348
1339
|
*/
|
|
1349
1340
|
async pullAgentReadinessSnapshot(nodeId) {
|
|
1350
1341
|
try {
|
|
1351
|
-
const snapshot = await this.
|
|
1342
|
+
const snapshot = await this.broker.call(`${AGENT_READINESS_SERVICE_NAME}.getSnapshot`, {}, { nodeID: nodeId });
|
|
1352
1343
|
if (!Array.isArray(snapshot) || snapshot.length === 0)
|
|
1353
1344
|
return;
|
|
1354
|
-
const records =
|
|
1345
|
+
const records = filterAgentAuthoritativeRecords(snapshot, nodeId);
|
|
1355
1346
|
if (records.length === 0)
|
|
1356
1347
|
return;
|
|
1357
1348
|
this.readinessRegistry.hydrate(records);
|
|
@@ -1367,19 +1358,18 @@ class MoleculerService {
|
|
|
1367
1358
|
}
|
|
1368
1359
|
}
|
|
1369
1360
|
async onModuleDestroy() {
|
|
1370
|
-
|
|
1371
|
-
|
|
1361
|
+
setProcessMeshTlsEnabler(null);
|
|
1362
|
+
setProcessMeshPeerTransportLookup(null);
|
|
1372
1363
|
if (this.agentReadinessPullTimer !== null) {
|
|
1373
1364
|
clearInterval(this.agentReadinessPullTimer);
|
|
1374
1365
|
this.agentReadinessPullTimer = null;
|
|
1375
1366
|
}
|
|
1376
1367
|
this.udsEventBridgeDispose?.();
|
|
1377
1368
|
this.udsEventBridgeDispose = null;
|
|
1378
|
-
await this.
|
|
1369
|
+
await this.broker.stop();
|
|
1379
1370
|
await this.localChildRegistry?.close();
|
|
1380
1371
|
}
|
|
1381
1372
|
}
|
|
1382
|
-
exports.MoleculerService = MoleculerService;
|
|
1383
1373
|
// ---------------------------------------------------------------------------
|
|
1384
1374
|
// Module-level helpers
|
|
1385
1375
|
// ---------------------------------------------------------------------------
|
|
@@ -1392,7 +1382,7 @@ exports.MoleculerService = MoleculerService;
|
|
|
1392
1382
|
* regression suite: the rule is only worth anything if register and unregister
|
|
1393
1383
|
* derive the token the SAME way.
|
|
1394
1384
|
*/
|
|
1395
|
-
function childOwnerToken(childNodeId, incarnation) {
|
|
1385
|
+
export function childOwnerToken(childNodeId, incarnation) {
|
|
1396
1386
|
return `${childNodeId}#${incarnation}`;
|
|
1397
1387
|
}
|
|
1398
1388
|
/**
|
|
@@ -1402,7 +1392,7 @@ function childOwnerToken(childNodeId, incarnation) {
|
|
|
1402
1392
|
* re-registering, and those two want opposite investigations. A line per skip
|
|
1403
1393
|
* would reproduce exactly the volume the gate exists to remove.
|
|
1404
1394
|
*/
|
|
1405
|
-
|
|
1395
|
+
export const CHILD_MANIFEST_SKIP_SAMPLE = 25;
|
|
1406
1396
|
/** Canonical, order-insensitive text for a child manifest. */
|
|
1407
1397
|
function childManifestFingerprint(params) {
|
|
1408
1398
|
const addons = params.addons
|
|
@@ -1436,7 +1426,7 @@ function childManifestFingerprint(params) {
|
|
|
1436
1426
|
* every provider owned by the dead generation and the successor's disconnect
|
|
1437
1427
|
* would unregister nothing.
|
|
1438
1428
|
*/
|
|
1439
|
-
class ChildManifestGate {
|
|
1429
|
+
export class ChildManifestGate {
|
|
1440
1430
|
applied = new Map();
|
|
1441
1431
|
skipped = 0;
|
|
1442
1432
|
sinceSample = 0;
|
|
@@ -1466,13 +1456,12 @@ class ChildManifestGate {
|
|
|
1466
1456
|
}
|
|
1467
1457
|
/** True once every {@link CHILD_MANIFEST_SKIP_SAMPLE} skips. */
|
|
1468
1458
|
sampleDue() {
|
|
1469
|
-
if (this.sinceSample <
|
|
1459
|
+
if (this.sinceSample < CHILD_MANIFEST_SKIP_SAMPLE)
|
|
1470
1460
|
return false;
|
|
1471
1461
|
this.sinceSample = 0;
|
|
1472
1462
|
return true;
|
|
1473
1463
|
}
|
|
1474
1464
|
}
|
|
1475
|
-
exports.ChildManifestGate = ChildManifestGate;
|
|
1476
1465
|
/**
|
|
1477
1466
|
* E1: Adapt a child's UDS `ChildCapDescriptor[]` into a `RegisterNodeParams`
|
|
1478
1467
|
* that `onRegisterNode` / `applyNodeManifest` can consume.
|
|
@@ -1494,7 +1483,7 @@ exports.ChildManifestGate = ChildManifestGate;
|
|
|
1494
1483
|
* cap names; device-scoped native caps are handled separately via `nativeCaps` in the
|
|
1495
1484
|
* full `RegisterNodeParams`.
|
|
1496
1485
|
*/
|
|
1497
|
-
function buildChildUdsManifest(nodeId, childId, caps) {
|
|
1486
|
+
export function buildChildUdsManifest(nodeId, childId, caps) {
|
|
1498
1487
|
// Group unique system (non-device-scoped) cap names per hosted addon.
|
|
1499
1488
|
// Insertion order preserves the child's registration order.
|
|
1500
1489
|
const capsByAddon = new Map();
|