@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
package/dist/agent/main.js
CHANGED
|
@@ -1,79 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.startAgent = startAgent;
|
|
37
|
-
const fs = __importStar(require("node:fs"));
|
|
38
|
-
const runner_boot_check_js_1 = require("../core/runner-health/runner-boot-check.js");
|
|
39
|
-
const path = __importStar(require("node:path"));
|
|
40
|
-
const system_1 = require("@camstack/system");
|
|
41
|
-
const types_1 = require("@camstack/types");
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import { RunnerBootCheckService, } from '../core/runner-health/runner-boot-check.js';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { AddonInstaller, AddonLoader, AGENT_READINESS_SERVICE_NAME, buildNodeManifest, CapabilityRegistry, createAddonContext, createAddonService, buildTransporter, connectBrokerTransport, createAgentTransport, createBroker, currentBrokerTransporter, disconnectBrokerTransport, installBrokerTransporter, isInstalledTransporter, createLocalTransport, createParentUnownedCallHandler, createProcessService, createReadinessServiceForRegistry, createUdsEventBridge, deriveAgentListenPort, detectWorkspacePackagesDir, getBrokerEventBus, getOrInitReadinessRegistry, HUB_CAP_FWD_ACTION, HubNodeRegistry, LocalChildRegistry, localEndpointPath, registerEventBusService, setNodeEventInterest, subscribePassthrough, udsChildLogToWorkerEntry, redactLogMeta, redactLogText, AgentMdnsAdvertiser, AgentPairing, createCiaoPublisher, TlsTcpTransporter, } from '@camstack/system';
|
|
5
|
+
import { EventCategory } from '@camstack/types';
|
|
42
6
|
// Capability definitions — must be declared before addon registerProvider() calls
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
7
|
+
import { ALL_CAPABILITY_DEFINITIONS, audioAnalyzerCapability, dataStoreProviderCapability, decoderCapability, errMsg, filesystemBrowseCapability, isDeployableToAgent, logDestinationCapability, metricsProviderCapability, motionDetectionCapability, nodeCertificatesCapability, normalizeAddonInitResult, pipelineExecutorCapability, pipelineRunnerCapability, platformProbeCapability, resolveAddonPlacement, resolveRunnerId, serverManagementCapability, settingsStoreCapability, storageCapability, storageProviderCapability, } from '@camstack/types';
|
|
8
|
+
import { buildServerManagementProvider } from '../api/core/server-management.provider.js';
|
|
9
|
+
import { resolveHubClosurePackageDir } from '../core/addon/package-dir-utils.js';
|
|
10
|
+
import { runSingleCopyCleanup } from '../single-copy-cleanup-runner.js';
|
|
11
|
+
import { BUILTINS_PACKAGE, planAgentAddonScan, resolveAddonPackageDirs } from './addon-scan.js';
|
|
12
|
+
import { buildAgentAddonUpdateReport } from './agent-addon-updates.js';
|
|
13
|
+
import { createAgentHubRegistration } from './agent-hub-registration.js';
|
|
14
|
+
import { createAgentMeshGate } from './agent-mesh-gate.js';
|
|
15
|
+
import { createLocalPairingAnnouncer } from './agent-pairing-local.js';
|
|
16
|
+
import { forgetPairing, isAgentPaired, pairedLeafFingerprint, persistPairing, } from './agent-pairing-store.js';
|
|
17
|
+
import { loadAgentConfig } from './agent-config.js';
|
|
18
|
+
import { ensureGroupRunner, isGroupRunnerAddon, readPackageManifestAddons, registerGroupRunnerProviders, } from './agent-group-runner.js';
|
|
19
|
+
import { startAgentHttpServer } from './agent-http.js';
|
|
20
|
+
import { createAgentNodeCertificates } from './agent-node-certificates.js';
|
|
21
|
+
import { createReconnectRunner } from './agent-reconnect.js';
|
|
22
|
+
import { createSerialQueue } from './serial-queue.js';
|
|
23
|
+
import { resolveAgentInfraCapabilities } from './agent-infra-capabilities.js';
|
|
24
|
+
import { createAgentService } from './agent-service.js';
|
|
25
|
+
import { createAgentHubAddonControls } from './agent-hub-addon-update.js';
|
|
26
|
+
import { createAgentServerUpdateControls } from './agent-update-routes.js';
|
|
27
|
+
import { AGENT_RUNTIME_ADDON_ID, AgentUpdateService, agentRuntimeManifestEntry, } from './agent-update-service.js';
|
|
28
|
+
import { deriveHubUrlForExport } from './derive-hub-url.js';
|
|
29
|
+
import { assertRequiredInfraResolved } from './infra-boot-guard.js';
|
|
30
|
+
import { planInfraBoot, planToResolutions } from './infra-boot-plan.js';
|
|
31
|
+
import { registerAgentCapDispatch } from './register-agent-cap-dispatch.js';
|
|
68
32
|
// ---------------------------------------------------------------------------
|
|
69
33
|
// Agent LogManager — shared log pipeline for all addon loggers.
|
|
70
34
|
// The hub-forwarder addon registers as a destination, so all log
|
|
71
35
|
// entries flow through it (console output + hub forwarding).
|
|
72
36
|
// ---------------------------------------------------------------------------
|
|
73
|
-
|
|
37
|
+
import { LogManager, systemHostFacts } from '@camstack/system';
|
|
74
38
|
/** A reconnect (stop + rebuild + start) that takes longer than this is abandoned. */
|
|
75
39
|
const RECONNECT_DEADLINE_MS = 90_000;
|
|
76
|
-
const agentLogManager = new
|
|
40
|
+
const agentLogManager = new LogManager(5000);
|
|
77
41
|
/**
|
|
78
42
|
* Caps whose `nodeId` arg is provider DATA (`nodeIdMode: 'data'` —
|
|
79
43
|
* `addon-settings`, `addons`, `nodes`, `pipeline-orchestrator`), never a
|
|
@@ -85,12 +49,12 @@ const agentLogManager = new system_2.LogManager(5000);
|
|
|
85
49
|
* the hub singleton (which reads `nodeId` as data) instead of being pinned to
|
|
86
50
|
* a node with no provider (dead broker fallback → ServiceNotFoundError).
|
|
87
51
|
*/
|
|
88
|
-
const DATA_NODEID_CAPS = new Set(
|
|
52
|
+
const DATA_NODEID_CAPS = new Set(ALL_CAPABILITY_DEFINITIONS.filter((c) => c.nodeIdMode === 'data').map((c) => c.name));
|
|
89
53
|
// ---------------------------------------------------------------------------
|
|
90
54
|
// Main
|
|
91
55
|
// ---------------------------------------------------------------------------
|
|
92
56
|
async function startAgent(configPath) {
|
|
93
|
-
const config =
|
|
57
|
+
const config = loadAgentConfig(configPath);
|
|
94
58
|
// Derive CAMSTACK_HUB_URL from the configured hub address so remote agents
|
|
95
59
|
// no longer need a second, redundant env var set by hand (the cross-node
|
|
96
60
|
// source trap). An explicit operator value ALWAYS wins — capture whether it
|
|
@@ -99,7 +63,7 @@ async function startAgent(configPath) {
|
|
|
99
63
|
// loadDeployedAddons below), and process-service spreads `process.env` into
|
|
100
64
|
// child env, so pipeline-runner + recorder children inherit it for free.
|
|
101
65
|
const hubUrlWasExplicit = process.env['CAMSTACK_HUB_URL'] !== undefined;
|
|
102
|
-
const derivedHubUrl =
|
|
66
|
+
const derivedHubUrl = deriveHubUrlForExport(hubUrlWasExplicit, config.hubAddress);
|
|
103
67
|
if (derivedHubUrl !== undefined) {
|
|
104
68
|
process.env['CAMSTACK_HUB_URL'] = derivedHubUrl;
|
|
105
69
|
console.log(`[Agent] Derived CAMSTACK_HUB_URL=${derivedHubUrl} from hub address "${config.hubAddress}"`);
|
|
@@ -130,14 +94,14 @@ async function startAgent(configPath) {
|
|
|
130
94
|
console.log(`[Agent] Using bundled addons from ${bundledDir}`);
|
|
131
95
|
}
|
|
132
96
|
else if (explicitSource === 'local') {
|
|
133
|
-
workspaceDir =
|
|
97
|
+
workspaceDir = detectWorkspacePackagesDir(config.dataDir);
|
|
134
98
|
}
|
|
135
|
-
const installer = new
|
|
99
|
+
const installer = new AddonInstaller({
|
|
136
100
|
addonsDir: config.addonsDir,
|
|
137
101
|
workspacePackagesDir: workspaceDir ?? undefined,
|
|
138
102
|
installSource: resolvedSource,
|
|
139
103
|
});
|
|
140
|
-
await installer.ensureRequiredPackages(
|
|
104
|
+
await installer.ensureRequiredPackages(AddonInstaller.AGENT_PACKAGES);
|
|
141
105
|
// Restore repair, mirroring the hub launcher (D338): reinstall ONLY the
|
|
142
106
|
// runtime deps that are neither present nor resolvable from the closure.
|
|
143
107
|
if (typeof installer.ensureAddonRuntimeDeps === 'function') {
|
|
@@ -159,11 +123,11 @@ async function startAgent(configPath) {
|
|
|
159
123
|
// D802, D857: the inbound mesh gate. Only the hub may call this agent, and the hub is
|
|
160
124
|
// recognised by the certificate the agent's own TLS listener verified. The transport is
|
|
161
125
|
// read at call time because a reconnect replaces it.
|
|
162
|
-
const meshGate =
|
|
126
|
+
const meshGate = createAgentMeshGate({
|
|
163
127
|
nodeId: config.nodeId,
|
|
164
128
|
isVerifiedPeer: (nodeId) => {
|
|
165
|
-
const tx =
|
|
166
|
-
return tx instanceof
|
|
129
|
+
const tx = currentBrokerTransporter(broker).tx;
|
|
130
|
+
return tx instanceof TlsTcpTransporter && tx.verifiedPeer(nodeId) !== undefined;
|
|
167
131
|
},
|
|
168
132
|
log: (msg) => console.log(`[Agent] ${msg}`),
|
|
169
133
|
});
|
|
@@ -173,7 +137,7 @@ async function startAgent(configPath) {
|
|
|
173
137
|
// The HTTP/HTTPS listener on the agent port (D856); set once it is up.
|
|
174
138
|
let agentHttpListener = null;
|
|
175
139
|
console.log(`[Agent] Mesh transport: ${currentMeshTls !== null ? 'tls (paired)' : 'none (unpaired)'}`);
|
|
176
|
-
const broker =
|
|
140
|
+
const broker = createBroker({
|
|
177
141
|
nodeID: config.nodeId,
|
|
178
142
|
mode: 'agent',
|
|
179
143
|
hubAddress: config.hubAddress,
|
|
@@ -191,7 +155,7 @@ async function startAgent(configPath) {
|
|
|
191
155
|
*/
|
|
192
156
|
// ONE reconnect at a time (serial-queue.ts): the UI, a mesh leaf swap and its rollback can
|
|
193
157
|
// all ask for one, and two interleaved disconnect/connect cycles would leave two wires or none.
|
|
194
|
-
const reconnect =
|
|
158
|
+
const reconnect = createSerialQueue(() => reconnectNow(), {
|
|
195
159
|
deadlineMs: RECONNECT_DEADLINE_MS,
|
|
196
160
|
onTimeout: () => {
|
|
197
161
|
// A transport whose connect never settles must not hold every later reconnect (including
|
|
@@ -200,26 +164,26 @@ async function startAgent(configPath) {
|
|
|
200
164
|
consoleLogger.error('Reconnect did not finish in time - dropping the half-connected transport', {
|
|
201
165
|
meta: { deadlineMs: RECONNECT_DEADLINE_MS },
|
|
202
166
|
});
|
|
203
|
-
void
|
|
167
|
+
void disconnectBrokerTransport(broker).catch(() => undefined);
|
|
204
168
|
},
|
|
205
169
|
});
|
|
206
170
|
// The cycle itself runs through a generation guard: a run the queue abandoned at its deadline
|
|
207
171
|
// that resumes later installs nothing (agent-reconnect.ts), so two transports never share a broker.
|
|
208
172
|
let pendingConfig = null;
|
|
209
|
-
const reconnectNow =
|
|
210
|
-
current: () =>
|
|
211
|
-
assign: (next) =>
|
|
173
|
+
const reconnectNow = createReconnectRunner({
|
|
174
|
+
current: () => currentBrokerTransporter(broker),
|
|
175
|
+
assign: (next) => installBrokerTransporter(broker, next),
|
|
212
176
|
stop: async (target) => {
|
|
213
177
|
// Only the transport installed right now: a late, abandoned run must not tear down a
|
|
214
178
|
// newer run's transport.
|
|
215
|
-
if (
|
|
216
|
-
await
|
|
179
|
+
if (isInstalledTransporter(broker, target)) {
|
|
180
|
+
await disconnectBrokerTransport(broker);
|
|
217
181
|
}
|
|
218
182
|
},
|
|
219
183
|
prepare: () => {
|
|
220
184
|
console.log('[Agent] Reconnecting with updated config...');
|
|
221
185
|
// Reload config from file (pairing, the UI or a local call may have written it)
|
|
222
|
-
const fresh =
|
|
186
|
+
const fresh = loadAgentConfig(undefined, config.dataDir);
|
|
223
187
|
pendingConfig = fresh;
|
|
224
188
|
console.log(`[Agent] New config: hub=${fresh.hubAddress ?? 'none'}, ` +
|
|
225
189
|
`mesh=${fresh.meshTls !== undefined ? 'tls (paired)' : 'none (unpaired)'}`);
|
|
@@ -229,7 +193,7 @@ async function startAgent(configPath) {
|
|
|
229
193
|
// keep the env they inherited; picking up a changed hub address in live
|
|
230
194
|
// children would need a runner respawn. Boot-time — the actual trap — is
|
|
231
195
|
// fully covered.
|
|
232
|
-
const freshHubUrl =
|
|
196
|
+
const freshHubUrl = deriveHubUrlForExport(hubUrlWasExplicit, fresh.hubAddress);
|
|
233
197
|
if (freshHubUrl !== undefined && process.env['CAMSTACK_HUB_URL'] !== freshHubUrl) {
|
|
234
198
|
process.env['CAMSTACK_HUB_URL'] = freshHubUrl;
|
|
235
199
|
console.log(`[Agent] Derived CAMSTACK_HUB_URL=${freshHubUrl} from hub address "${fresh.hubAddress}"`);
|
|
@@ -240,7 +204,7 @@ async function startAgent(configPath) {
|
|
|
240
204
|
const fresh = pendingConfig;
|
|
241
205
|
if (fresh === null)
|
|
242
206
|
throw new Error('reconnect: no configuration was prepared');
|
|
243
|
-
return
|
|
207
|
+
return buildTransporter(createAgentTransport({
|
|
244
208
|
nodeID: config.nodeId,
|
|
245
209
|
mode: 'agent',
|
|
246
210
|
hubAddress: fresh.hubAddress,
|
|
@@ -251,7 +215,7 @@ async function startAgent(configPath) {
|
|
|
251
215
|
// The new wire's hub connection re-sends the manifest (`registration.onHubConnected`):
|
|
252
216
|
// the subtree union, every child included (D3).
|
|
253
217
|
start: async () => {
|
|
254
|
-
await
|
|
218
|
+
await connectBrokerTransport(broker);
|
|
255
219
|
console.log('[Agent] Reconnected successfully');
|
|
256
220
|
},
|
|
257
221
|
onSuperseded: (stage) => consoleLogger.warn('A superseded reconnect stopped without installing a second transport', {
|
|
@@ -262,7 +226,7 @@ async function startAgent(configPath) {
|
|
|
262
226
|
// D3 subtree registry: accumulates manifests from every group-runner child
|
|
263
227
|
// that calls `$agent.registerNode`. The agent re-registers the UNION of its
|
|
264
228
|
// own in-process addons + all children with the hub on every child update.
|
|
265
|
-
const subtree = new
|
|
229
|
+
const subtree = new HubNodeRegistry();
|
|
266
230
|
// ---------------------------------------------------------------------------
|
|
267
231
|
// Hub connection state tracking (for richer UI status)
|
|
268
232
|
// ---------------------------------------------------------------------------
|
|
@@ -279,15 +243,14 @@ async function startAgent(configPath) {
|
|
|
279
243
|
// Fall through to live registry-derived state (connected/unconfigured/disconnected).
|
|
280
244
|
// getRegistryNodes-equivalent inline: broker.registry.getNodeList
|
|
281
245
|
try {
|
|
282
|
-
const
|
|
283
|
-
const nodes = registry?.getNodeList?.({ onlyAvailable: true }) ?? [];
|
|
246
|
+
const nodes = broker.registry.getNodeList({ onlyAvailable: true });
|
|
284
247
|
if (nodes.some((n) => n.id === 'hub'))
|
|
285
248
|
return 'connected';
|
|
286
249
|
}
|
|
287
250
|
catch {
|
|
288
251
|
/* fall through */
|
|
289
252
|
}
|
|
290
|
-
if (!
|
|
253
|
+
if (!isAgentPaired(config.configPath))
|
|
291
254
|
return 'unpaired';
|
|
292
255
|
const configNow = readConfigFile(config.configPath);
|
|
293
256
|
const hubAddressConfigured = typeof configNow.hubAddress === 'string' && configNow.hubAddress.length > 0;
|
|
@@ -349,7 +312,7 @@ async function startAgent(configPath) {
|
|
|
349
312
|
// The agent runtime's OWN providers (server-management) are registered by
|
|
350
313
|
// the bootstrap, not an addon — append their synthetic manifest entry so
|
|
351
314
|
// the hub's HubNodeRegistry (and nodeId-pinned cap routing) sees them.
|
|
352
|
-
result.push(
|
|
315
|
+
result.push(agentRuntimeManifestEntry());
|
|
353
316
|
return result;
|
|
354
317
|
}
|
|
355
318
|
/**
|
|
@@ -374,7 +337,7 @@ async function startAgent(configPath) {
|
|
|
374
337
|
// carries the agent's root-package identity so the hub's HubNodeRegistry
|
|
375
338
|
// (and the Server management UI) sees every node's exact version.
|
|
376
339
|
const rootPackage = agentUpdateService.getRunningRootPackage();
|
|
377
|
-
return
|
|
340
|
+
return buildNodeManifest(config.nodeId, allAddons, allNativeCaps.length > 0 ? allNativeCaps : undefined, rootPackage.version === null
|
|
378
341
|
? undefined
|
|
379
342
|
: { name: rootPackage.name, version: rootPackage.version });
|
|
380
343
|
}
|
|
@@ -388,7 +351,7 @@ async function startAgent(configPath) {
|
|
|
388
351
|
* D857: no join to wait for and no secret in the manifest — the hub knows
|
|
389
352
|
* who this agent is from its certificate.
|
|
390
353
|
*/
|
|
391
|
-
const registration =
|
|
354
|
+
const registration = createAgentHubRegistration({
|
|
392
355
|
buildManifest: () => aggregateManifest(),
|
|
393
356
|
broker: () => broker,
|
|
394
357
|
signal: registerAbortController.signal,
|
|
@@ -416,36 +379,36 @@ async function startAgent(configPath) {
|
|
|
416
379
|
child: () => consoleLogger,
|
|
417
380
|
withTags: () => consoleLogger,
|
|
418
381
|
};
|
|
419
|
-
const capabilityRegistry = new
|
|
382
|
+
const capabilityRegistry = new CapabilityRegistry(consoleLogger);
|
|
420
383
|
// Declare all capabilities the agent uses — required before registerProvider()
|
|
421
384
|
const agentCapabilities = [
|
|
422
|
-
|
|
423
|
-
|
|
385
|
+
storageCapability,
|
|
386
|
+
storageProviderCapability,
|
|
424
387
|
// The path browser for "add a storage location on this node" — the agent
|
|
425
388
|
// registers the provider since day one, but without DECLARING the cap
|
|
426
389
|
// here the hub's manifest never learns of it and node-routed browse
|
|
427
390
|
// answers "not available on node X" (2026-07-30 fix).
|
|
428
|
-
|
|
391
|
+
filesystemBrowseCapability,
|
|
429
392
|
// The agent runs the same two-addon data topology as the hub (D44):
|
|
430
393
|
// `settings-store` is the door served by storage-orchestrator,
|
|
431
394
|
// `data-store-provider` is the engine sqlite-settings registers. BOTH
|
|
432
395
|
// must be declared here — this array is hand-curated and nothing else
|
|
433
396
|
// teaches an agent a capability, so omitting the engine leaves the
|
|
434
397
|
// agent's door with nothing behind it.
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
398
|
+
settingsStoreCapability,
|
|
399
|
+
dataStoreProviderCapability,
|
|
400
|
+
logDestinationCapability,
|
|
401
|
+
metricsProviderCapability,
|
|
402
|
+
decoderCapability,
|
|
403
|
+
motionDetectionCapability,
|
|
404
|
+
pipelineExecutorCapability,
|
|
405
|
+
pipelineRunnerCapability,
|
|
406
|
+
audioAnalyzerCapability,
|
|
407
|
+
platformProbeCapability,
|
|
408
|
+
serverManagementCapability,
|
|
446
409
|
// Certificates of THIS node, called by the hub (D856): mesh leaf renewal and the
|
|
447
410
|
// hub-issued HTTPS leaf. Provided by the runtime, like `server-management`.
|
|
448
|
-
|
|
411
|
+
nodeCertificatesCapability,
|
|
449
412
|
];
|
|
450
413
|
for (const cap of agentCapabilities) {
|
|
451
414
|
capabilityRegistry.declareCapability(cap);
|
|
@@ -461,16 +424,16 @@ async function startAgent(configPath) {
|
|
|
461
424
|
// agent runs, so the agent compares what its process service holds (a runner
|
|
462
425
|
// that failed or refused is named; one that never spawned is not knowable here).
|
|
463
426
|
let agentRunnerBootCheck = null;
|
|
464
|
-
const agentUpdateService = new
|
|
427
|
+
const agentUpdateService = new AgentUpdateService({
|
|
465
428
|
logger: consoleLogger,
|
|
466
429
|
dataDir: config.dataDir,
|
|
467
430
|
runnerBootCheck: () => agentRunnerBootCheck?.current(),
|
|
468
431
|
});
|
|
469
|
-
capabilityRegistry.registerProvider('server-management',
|
|
432
|
+
capabilityRegistry.registerProvider('server-management', AGENT_RUNTIME_ADDON_ID, buildServerManagementProvider(agentUpdateService));
|
|
470
433
|
// The agent's certificates (D856): mesh leaf swap with automatic rollback, and the
|
|
471
434
|
// hub-issued HTTPS leaf hot-swapped into the sniffing listener. The listener is
|
|
472
435
|
// created later in boot, so it is read through a getter.
|
|
473
|
-
capabilityRegistry.registerProvider('node-certificates',
|
|
436
|
+
capabilityRegistry.registerProvider('node-certificates', AGENT_RUNTIME_ADDON_ID, createAgentNodeCertificates({
|
|
474
437
|
nodeId: config.nodeId,
|
|
475
438
|
dataDir: config.dataDir,
|
|
476
439
|
configPath: config.configPath,
|
|
@@ -481,7 +444,7 @@ async function startAgent(configPath) {
|
|
|
481
444
|
timer.unref?.();
|
|
482
445
|
},
|
|
483
446
|
getHttpsListener: () => agentHttpListener,
|
|
484
|
-
hostFacts:
|
|
447
|
+
hostFacts: systemHostFacts(),
|
|
485
448
|
log: {
|
|
486
449
|
info: (message, extras) => consoleLogger.info(message, extras),
|
|
487
450
|
warn: (message, extras) => consoleLogger.warn(message, extras),
|
|
@@ -513,7 +476,7 @@ async function startAgent(configPath) {
|
|
|
513
476
|
// — a copy nothing loads (both roles pass `['@camstack/system']` as the
|
|
514
477
|
// loader's skip list) and that WINS over the closure for anything resolving
|
|
515
478
|
// through it.
|
|
516
|
-
void
|
|
479
|
+
void runSingleCopyCleanup({
|
|
517
480
|
dataDir: config.dataDir,
|
|
518
481
|
addonRoot: config.addonsDir,
|
|
519
482
|
log: (line) => consoleLogger.info(line),
|
|
@@ -524,7 +487,7 @@ async function startAgent(configPath) {
|
|
|
524
487
|
// No scope — the brand bracket `[agent/addonId]` already identifies the addon.
|
|
525
488
|
// `addonId` tag is required for the brand bracket resolver.
|
|
526
489
|
const loggerFactory = (addonId) => agentLogManager.createLogger().withTags({ addonId });
|
|
527
|
-
const agentServiceSchema =
|
|
490
|
+
const agentServiceSchema = createAgentService({
|
|
528
491
|
addonsDir: config.addonsDir,
|
|
529
492
|
dataDir: config.dataDir,
|
|
530
493
|
agentName: config.name,
|
|
@@ -582,7 +545,7 @@ async function startAgent(configPath) {
|
|
|
582
545
|
bootstrapPackages: new Set([
|
|
583
546
|
'@camstack/system',
|
|
584
547
|
'@camstack/addon-agent-ui',
|
|
585
|
-
...
|
|
548
|
+
...AddonInstaller.AGENT_PACKAGES,
|
|
586
549
|
]),
|
|
587
550
|
unregisterProvider: (capName, addonId) => {
|
|
588
551
|
if (capabilityRegistry.hasProvider(capName, addonId)) {
|
|
@@ -596,7 +559,7 @@ async function startAgent(configPath) {
|
|
|
596
559
|
// back here instead of falling back to port 6000 (the hub). Bug-4:
|
|
597
560
|
// without this, runners called `$agent.registerNode` through the hub
|
|
598
561
|
// which has no `$agent` service → retry storm (attempt 80+).
|
|
599
|
-
const agentTcpPort =
|
|
562
|
+
const agentTcpPort = deriveAgentListenPort(broker.nodeID);
|
|
600
563
|
// UDS local transport — the agent hosts a LocalChildRegistry so its
|
|
601
564
|
// forked addon-runners route cap calls over a Unix-domain socket. The
|
|
602
565
|
// broker stays as the no-route fallback. On failure the children fall
|
|
@@ -615,7 +578,7 @@ async function startAgent(configPath) {
|
|
|
615
578
|
// mesh (and any cluster node) via the same service-discovery + action-name
|
|
616
579
|
// convention the child's brokerTransportLink used before F0. F1+F2 removes
|
|
617
580
|
// the child broker, so the agent must own this outbound path.
|
|
618
|
-
const onUnownedCall =
|
|
581
|
+
const onUnownedCall = createParentUnownedCallHandler({
|
|
619
582
|
getResolver: () => null,
|
|
620
583
|
broker: broker,
|
|
621
584
|
// The agent's subtree registry — lets the broker fallback pin a
|
|
@@ -638,7 +601,7 @@ async function startAgent(configPath) {
|
|
|
638
601
|
// makes an agent's forked addon reach a hub-hosted cap (`stream-broker`,
|
|
639
602
|
// `settings-store`, …) instead of 30s-deadlining on raw `${cap}.*`
|
|
640
603
|
// discovery (hub-local addon runners expose no Moleculer service).
|
|
641
|
-
forwardToHub: (input) => broker.call(
|
|
604
|
+
forwardToHub: (input) => broker.call(HUB_CAP_FWD_ACTION, {
|
|
642
605
|
capName: input.capName,
|
|
643
606
|
method: input.method,
|
|
644
607
|
args: input.args,
|
|
@@ -657,8 +620,8 @@ async function startAgent(configPath) {
|
|
|
657
620
|
}, { timeout: 60_000 }),
|
|
658
621
|
logger: { warn: (msg) => consoleLogger.warn(`[uds-fallback] ${msg}`) },
|
|
659
622
|
});
|
|
660
|
-
agentUdsRegistry = new
|
|
661
|
-
server:
|
|
623
|
+
agentUdsRegistry = new LocalChildRegistry({
|
|
624
|
+
server: createLocalTransport().createServer(agentNodeId),
|
|
662
625
|
// `WorkerOnline` / `WorkerOffline` `<agent>/<childId>` for a forked runner
|
|
663
626
|
// (no broker of its own, so no `$node.connected`), on the agent's
|
|
664
627
|
// cluster-broadcasting bus. The bus exists only after `broker.start()`;
|
|
@@ -725,11 +688,11 @@ async function startAgent(configPath) {
|
|
|
725
688
|
agentUdsRegistry.onChildLog((childId, entry) => {
|
|
726
689
|
// Key names and URL credentials redacted before the line leaves this
|
|
727
690
|
// node (D787 §8). The child already scrubbed the values it resolved.
|
|
728
|
-
const raw =
|
|
691
|
+
const raw = udsChildLogToWorkerEntry(childId, entry);
|
|
729
692
|
const workerEntry = {
|
|
730
693
|
...raw,
|
|
731
|
-
message:
|
|
732
|
-
...(raw.meta !== undefined ? { meta:
|
|
694
|
+
message: redactLogText(raw.message),
|
|
695
|
+
...(raw.meta !== undefined ? { meta: redactLogMeta(raw.meta) } : {}),
|
|
733
696
|
};
|
|
734
697
|
broker.call('log-receiver.ingest', workerEntry).catch(() => {
|
|
735
698
|
// Hub unreachable or not yet discovered — silently drop.
|
|
@@ -737,13 +700,13 @@ async function startAgent(configPath) {
|
|
|
737
700
|
// child UDS logs emitted before hub connection are not buffered here.
|
|
738
701
|
});
|
|
739
702
|
});
|
|
740
|
-
agentParentUdsPath =
|
|
703
|
+
agentParentUdsPath = localEndpointPath(agentNodeId);
|
|
741
704
|
consoleLogger.info(`UDS child registry listening on ${agentParentUdsPath}`);
|
|
742
705
|
}
|
|
743
706
|
catch (err) {
|
|
744
707
|
consoleLogger.warn(`UDS child registry failed to start; children stay broker-only: ${err instanceof Error ? err.message : String(err)}`);
|
|
745
708
|
}
|
|
746
|
-
const processServiceSchema =
|
|
709
|
+
const processServiceSchema = createProcessService(broker.nodeID, config.dataDir, undefined, agentTcpPort, agentParentUdsPath);
|
|
747
710
|
broker.createService(processServiceSchema);
|
|
748
711
|
// Slice-5, Task 7 — register the agent-side cap-dispatch service so the hub
|
|
749
712
|
// can route `agent-child-forward` cap calls to this agent's UDS children.
|
|
@@ -771,14 +734,14 @@ async function startAgent(configPath) {
|
|
|
771
734
|
},
|
|
772
735
|
};
|
|
773
736
|
};
|
|
774
|
-
|
|
737
|
+
registerAgentCapDispatch(broker, agentUdsRegistry, agentInProcessLookup, consoleLogger);
|
|
775
738
|
// $addonHost — REMOVED (Sprint 6). Three-level settings are now
|
|
776
739
|
// exposed per-addon via `settings.*` actions in createAddonService.
|
|
777
740
|
// Register $event-bus BEFORE start so the service subscription
|
|
778
741
|
// is announced during discovery. See addon-context-factory.ts for
|
|
779
742
|
// the rationale — post-start registration propagates via heartbeat
|
|
780
743
|
// and is unreliable for the first ~10s.
|
|
781
|
-
|
|
744
|
+
registerEventBusService(broker);
|
|
782
745
|
// Start broker BEFORE bootCoreAddons. Core infra addons (storage,
|
|
783
746
|
// sqlite-settings, ...) run their own BaseAddon.initialize() which
|
|
784
747
|
// calls `ctx.settings.readAddonStore()` on every addon; that call
|
|
@@ -788,7 +751,7 @@ async function startAgent(configPath) {
|
|
|
788
751
|
// discovery resolve (or time out cleanly via the read-blob fallback)
|
|
789
752
|
// so agent boot completes instead of hanging on the first infra addon.
|
|
790
753
|
await broker.start();
|
|
791
|
-
agentRunnerBootCheck = new
|
|
754
|
+
agentRunnerBootCheck = new RunnerBootCheckService({
|
|
792
755
|
logger: consoleLogger,
|
|
793
756
|
nodeId: config.nodeId,
|
|
794
757
|
planned: () => null,
|
|
@@ -800,25 +763,25 @@ async function startAgent(configPath) {
|
|
|
800
763
|
});
|
|
801
764
|
agentRunnerBootCheck.scheduleAfterBoot();
|
|
802
765
|
// Re-evaluate a verdict that still had runners on their way, on each runner state change.
|
|
803
|
-
|
|
766
|
+
getBrokerEventBus(broker).subscribe({ category: EventCategory.ProcessStateChanged }, () => agentRunnerBootCheck?.noteRunnerChange());
|
|
804
767
|
// C2: wire the UDS ↔ Moleculer event bridge so events emitted by UDS
|
|
805
768
|
// children fan to siblings and reach the cluster, and cluster / parent-
|
|
806
769
|
// local events propagate to every UDS child. Inert when agentUdsRegistry
|
|
807
770
|
// was not started (no UDS children). Wired after broker.start() so
|
|
808
771
|
// getBrokerEventBus returns the fully operational shared bus.
|
|
809
|
-
const agentBrokerEventBus =
|
|
772
|
+
const agentBrokerEventBus = getBrokerEventBus(broker);
|
|
810
773
|
agentLifecycleBus = agentBrokerEventBus;
|
|
811
774
|
agentUpdateService.bindAvailability(agentBrokerEventBus, broker.nodeID);
|
|
812
775
|
let udsEventBridgeDispose = null;
|
|
813
776
|
if (agentUdsRegistry !== undefined) {
|
|
814
|
-
udsEventBridgeDispose =
|
|
777
|
+
udsEventBridgeDispose = createUdsEventBridge({
|
|
815
778
|
registry: agentUdsRegistry,
|
|
816
779
|
parentBus: agentBrokerEventBus,
|
|
817
780
|
parentNodeId: broker.nodeID,
|
|
818
781
|
// D2: relay to children via the pass-through subscription so the bridge's
|
|
819
782
|
// wildcard does NOT count toward this node's local interest (otherwise the
|
|
820
783
|
// gate could never filter anything).
|
|
821
|
-
subscribePassthrough: (handler) =>
|
|
784
|
+
subscribePassthrough: (handler) => subscribePassthrough(broker, handler),
|
|
822
785
|
});
|
|
823
786
|
// D2: teach the agent's `$event-bus` inbound handler which cross-node
|
|
824
787
|
// (Moleculer) event categories any of its UDS children actually want, so the
|
|
@@ -827,7 +790,7 @@ async function startAgent(configPath) {
|
|
|
827
790
|
// (un)subscribing takes effect immediately. Fails OPEN when a child is
|
|
828
791
|
// undeclared (aggregateEventInterest → null).
|
|
829
792
|
const interestRegistry = agentUdsRegistry;
|
|
830
|
-
|
|
793
|
+
setNodeEventInterest(broker, () => interestRegistry.aggregateEventInterest());
|
|
831
794
|
// D1: answer readiness-snapshot requests from UDS children over the
|
|
832
795
|
// agent's own readiness registry view. The agent hydrates its registry
|
|
833
796
|
// from the hub's `$readiness.getSnapshot` Moleculer action (broker
|
|
@@ -838,35 +801,43 @@ async function startAgent(configPath) {
|
|
|
838
801
|
// uses, so the shared per-broker instance is returned on the first call
|
|
839
802
|
// and reused on subsequent calls — no separate registry is created.
|
|
840
803
|
// Wired after broker.start() so the broker event bus is operational.
|
|
841
|
-
const agentReadinessRegistry =
|
|
804
|
+
const agentReadinessRegistry = getOrInitReadinessRegistry(broker, agentBrokerEventBus, consoleLogger);
|
|
842
805
|
agentUdsRegistry.onReadinessSnapshotRequest(() => agentReadinessRegistry.getSnapshotForTransport());
|
|
843
|
-
|
|
806
|
+
// D8 reverse leg: expose THIS agent's readiness snapshot so the hub can
|
|
807
|
+
// snapshot-reconcile agent-origin records on every (re)connect and
|
|
808
|
+
// periodically. Without this, the hub learns agent readiness ONLY from
|
|
809
|
+
// lossy cross-node `system.ready-state` broadcasts — a lost `ready`
|
|
810
|
+
// (2026-07-17 incident) left the hub's authoritative registry stuck on
|
|
811
|
+
// the synthesized `down` forever. Registered under
|
|
812
|
+
// `$agent-readiness` (NOT `$readiness`) so the agent's own unqualified
|
|
813
|
+
// `$readiness.getSnapshot` hub-hydrate call is never shadowed locally.
|
|
814
|
+
broker.createService(createReadinessServiceForRegistry(agentReadinessRegistry, AGENT_READINESS_SERVICE_NAME));
|
|
844
815
|
}
|
|
845
816
|
// ── HTTP server (Fastify) — status API + process management + UI ──
|
|
846
817
|
// Started early (before addon boot) so the status page is reachable
|
|
847
818
|
// even while addons are still loading.
|
|
848
|
-
const getBrokerFn = (
|
|
819
|
+
const getBrokerFn = () => broker;
|
|
849
820
|
// ── Update controls for the agent UI ──────────────────────────────
|
|
850
821
|
// Framework: the LOCAL `AgentUpdateService` — `npm view` + `npm install`
|
|
851
822
|
// straight from the registry, no hub in the path, so an isolated node can
|
|
852
823
|
// repair its own version. Addons: report only (the install is a hub push;
|
|
853
824
|
// see `agent-addon-updates.ts`).
|
|
854
825
|
const updateControls = {
|
|
855
|
-
server:
|
|
826
|
+
server: createAgentServerUpdateControls(agentUpdateService, (message) => {
|
|
856
827
|
// A refused/failed update that logs nothing reads as "never happened".
|
|
857
828
|
consoleLogger.warn(`framework update not applied: ${message}`, {
|
|
858
829
|
tags: { nodeId: config.nodeId },
|
|
859
830
|
});
|
|
860
831
|
}),
|
|
861
832
|
addons: {
|
|
862
|
-
hub:
|
|
833
|
+
hub: createAgentHubAddonControls({
|
|
863
834
|
nodeId: config.nodeId,
|
|
864
835
|
// The mesh channel the agent already holds; the hub names this caller
|
|
865
836
|
// `node:<id>` and answers per its remote privilege grant.
|
|
866
837
|
callHub: (action, params, timeoutMs) => broker.call(action, params, { nodeID: 'hub', timeout: timeoutMs }),
|
|
867
838
|
onProblem: (message) => consoleLogger.warn(message, { tags: { nodeId: config.nodeId } }),
|
|
868
839
|
}),
|
|
869
|
-
getReport: () =>
|
|
840
|
+
getReport: () => buildAgentAddonUpdateReport({
|
|
870
841
|
addonsDir: config.addonsDir,
|
|
871
842
|
...(process.env['CAMSTACK_NPM_REGISTRY'] !== undefined
|
|
872
843
|
? { registryUrl: process.env['CAMSTACK_NPM_REGISTRY'] }
|
|
@@ -880,8 +851,8 @@ async function startAgent(configPath) {
|
|
|
880
851
|
// A hub pairs this agent from outside; the agent installs the certificate only after
|
|
881
852
|
// its own operator confirmed the code. Its pairing state is advertised over mDNS.
|
|
882
853
|
const agentVersion = process.env['CAMSTACK_AGENT_APP_VERSION'] ?? agentUpdateService.getRunningRootPackage().version;
|
|
883
|
-
const agentMdns = new
|
|
884
|
-
publisher:
|
|
854
|
+
const agentMdns = new AgentMdnsAdvertiser({
|
|
855
|
+
publisher: createCiaoPublisher(),
|
|
885
856
|
logger: consoleLogger,
|
|
886
857
|
});
|
|
887
858
|
const advertise = () => {
|
|
@@ -890,16 +861,16 @@ async function startAgent(configPath) {
|
|
|
890
861
|
version: agentVersion,
|
|
891
862
|
port: config.statusPort ?? 4444,
|
|
892
863
|
state: pairing.state(),
|
|
893
|
-
leafFingerprint:
|
|
864
|
+
leafFingerprint: pairedLeafFingerprint(config.configPath),
|
|
894
865
|
});
|
|
895
866
|
};
|
|
896
|
-
const localPairing =
|
|
897
|
-
const pairing = new
|
|
867
|
+
const localPairing = createLocalPairingAnnouncer(config.dataDir, consoleLogger);
|
|
868
|
+
const pairing = new AgentPairing({
|
|
898
869
|
nodeId: config.nodeId,
|
|
899
870
|
version: agentVersion,
|
|
900
|
-
isPaired: () =>
|
|
871
|
+
isPaired: () => isAgentPaired(config.configPath),
|
|
901
872
|
install: async (installed) => {
|
|
902
|
-
|
|
873
|
+
persistPairing(config.configPath, installed);
|
|
903
874
|
hubConnectionStateOverride = null;
|
|
904
875
|
consoleLogger.info('Paired with the hub - reconnecting with the new certificate', {
|
|
905
876
|
meta: { hubUrl: installed.hubUrl, hubAddress: installed.hubAddress },
|
|
@@ -917,14 +888,14 @@ async function startAgent(configPath) {
|
|
|
917
888
|
});
|
|
918
889
|
const forgetHubNow = async (why) => {
|
|
919
890
|
consoleLogger.warn(`Leaving the hub: ${why}. This agent is unpaired and advertises again`);
|
|
920
|
-
|
|
891
|
+
forgetPairing(config.configPath, config.dataDir);
|
|
921
892
|
pairing.noteUnpaired();
|
|
922
893
|
hubConnectionStateOverride = null;
|
|
923
|
-
await reconnect().catch((err) => consoleLogger.error(`reconnect after unpairing failed: ${
|
|
894
|
+
await reconnect().catch((err) => consoleLogger.error(`reconnect after unpairing failed: ${errMsg(err)}`));
|
|
924
895
|
advertise();
|
|
925
896
|
};
|
|
926
897
|
advertise();
|
|
927
|
-
void
|
|
898
|
+
void startAgentHttpServer(getBrokerFn, {
|
|
928
899
|
log: {
|
|
929
900
|
info: (message, extras) => consoleLogger.info(message, extras),
|
|
930
901
|
warn: (message, extras) => consoleLogger.warn(message, extras),
|
|
@@ -1033,7 +1004,7 @@ async function startAgent(configPath) {
|
|
|
1033
1004
|
// here would only open a database nothing reads. See
|
|
1034
1005
|
// `agent-infra-capabilities.ts` for the measurement behind that and for the
|
|
1035
1006
|
// one-env-var rollback (`CAMSTACK_AGENT_LOCAL_DATA_STORE=1`).
|
|
1036
|
-
const AGENT_INFRA =
|
|
1007
|
+
const AGENT_INFRA = resolveAgentInfraCapabilities(process.env);
|
|
1037
1008
|
async function bootCoreAddons(broker, config, registry, loadedAddons, loggerFactory) {
|
|
1038
1009
|
// Builtins come from the CLOSURE this process is running, exactly as the hub
|
|
1039
1010
|
// has done since 2026-07-18 — never from a `<addonsDir>/@camstack/system`
|
|
@@ -1041,27 +1012,27 @@ async function bootCoreAddons(broker, config, registry, loadedAddons, loggerFact
|
|
|
1041
1012
|
// 17 versions behind on 2026-08-11). `planAgentAddonScan` owns the decision
|
|
1042
1013
|
// and the fallback; see `addon-scan.ts`. Non-framework infra providers
|
|
1043
1014
|
// (e.g. `@camstack/addon-platform-probe-native`) still come from the scan.
|
|
1044
|
-
const closureSystemDir =
|
|
1015
|
+
const closureSystemDir = resolveHubClosurePackageDir(BUILTINS_PACKAGE);
|
|
1045
1016
|
if (closureSystemDir === null) {
|
|
1046
1017
|
// Louder than the hub's warn: on an agent the builtins ARE the infra, so
|
|
1047
1018
|
// this is pre-fatal. Boot continues to the D87 guard, which names every
|
|
1048
1019
|
// required cap it could not resolve.
|
|
1049
|
-
console.error(`[Agent] ${
|
|
1020
|
+
console.error(`[Agent] ${BUILTINS_PACKAGE} not resolvable from the running closure — ` +
|
|
1050
1021
|
'falling back to the addon-root copy, if there is one');
|
|
1051
1022
|
}
|
|
1052
|
-
const packageDirs =
|
|
1023
|
+
const packageDirs = planAgentAddonScan(config.addonsDir, closureSystemDir);
|
|
1053
1024
|
if (packageDirs.length === 0) {
|
|
1054
1025
|
console.warn('[Agent] No addon packages found — running without infrastructure addons');
|
|
1055
1026
|
return;
|
|
1056
1027
|
}
|
|
1057
1028
|
console.log(`[Agent] Scanning ${packageDirs.length} addon package(s) for infra providers`);
|
|
1058
|
-
const loader = new
|
|
1029
|
+
const loader = new AddonLoader();
|
|
1059
1030
|
for (const dir of packageDirs) {
|
|
1060
1031
|
try {
|
|
1061
1032
|
await loader.loadFromAddonDir(dir);
|
|
1062
1033
|
}
|
|
1063
1034
|
catch (err) {
|
|
1064
|
-
console.warn(`[Agent] Failed to scan ${dir}: ${
|
|
1035
|
+
console.warn(`[Agent] Failed to scan ${dir}: ${errMsg(err)}`);
|
|
1065
1036
|
}
|
|
1066
1037
|
}
|
|
1067
1038
|
// Resolution is per-CAPABILITY (the guard names every missing required
|
|
@@ -1073,11 +1044,11 @@ async function bootCoreAddons(broker, config, registry, loadedAddons, loggerFact
|
|
|
1073
1044
|
// 2026-08-08 the moment D87 promoted infra failures to fatal. The plan
|
|
1074
1045
|
// makes the dedupe explicit and testable (see `infra-boot-plan.ts`).
|
|
1075
1046
|
const byId = new Map(loader.listAddons().map((a) => [a.declaration.id, a]));
|
|
1076
|
-
const plan =
|
|
1047
|
+
const plan = planInfraBoot(loader.listAddons().map((a) => ({
|
|
1077
1048
|
id: a.declaration.id,
|
|
1078
1049
|
capabilities: (a.declaration.capabilities ?? []).map((c) => typeof c === 'string' ? c : c.name),
|
|
1079
1050
|
})), AGENT_INFRA);
|
|
1080
|
-
const resolutions =
|
|
1051
|
+
const resolutions = planToResolutions(plan);
|
|
1081
1052
|
for (const step of plan) {
|
|
1082
1053
|
if (step.addonId === null) {
|
|
1083
1054
|
// Say it per-capability (an OPTIONAL miss is the only thing this line
|
|
@@ -1103,7 +1074,7 @@ async function bootCoreAddons(broker, config, registry, loadedAddons, loggerFact
|
|
|
1103
1074
|
// in the registry (the storage addon declares itself before the
|
|
1104
1075
|
// addons that depend on it per AGENT_INFRA order).
|
|
1105
1076
|
const storageProvider = registry.getSingleton('storage') ?? undefined;
|
|
1106
|
-
const context = await
|
|
1077
|
+
const context = await createAddonContext(broker, addon.declaration, config.dataDir, {
|
|
1107
1078
|
storageProvider,
|
|
1108
1079
|
addonConfig: { rootPath: config.dataDir },
|
|
1109
1080
|
createLogger: loggerFactory,
|
|
@@ -1114,7 +1085,7 @@ async function bootCoreAddons(broker, config, registry, loadedAddons, loggerFact
|
|
|
1114
1085
|
// configured for type addons-data" → fatal crash-loop.
|
|
1115
1086
|
listStorageLocationDeclarations: () => loader.listStorageLocationDeclarations(),
|
|
1116
1087
|
});
|
|
1117
|
-
const initResult =
|
|
1088
|
+
const initResult = normalizeAddonInitResult(await instance.initialize(context));
|
|
1118
1089
|
for (const reg of initResult?.providers ?? []) {
|
|
1119
1090
|
const capName = reg.capability.name;
|
|
1120
1091
|
registry.registerProvider(capName, addonId, reg.provider);
|
|
@@ -1132,7 +1103,7 @@ async function bootCoreAddons(broker, config, registry, loadedAddons, loggerFact
|
|
|
1132
1103
|
console.log(`[Agent] Core addon "${addonId}" initialized`);
|
|
1133
1104
|
}
|
|
1134
1105
|
catch (err) {
|
|
1135
|
-
const msg =
|
|
1106
|
+
const msg = errMsg(err);
|
|
1136
1107
|
console.error(`[Agent] Failed to initialize core addon "${addonId}": ${msg}`);
|
|
1137
1108
|
if (step.required) {
|
|
1138
1109
|
throw new Error(`Required infrastructure addon "${addonId}" failed: ${msg}`, { cause: err });
|
|
@@ -1144,7 +1115,7 @@ async function bootCoreAddons(broker, config, registry, loadedAddons, loggerFact
|
|
|
1144
1115
|
// the SILENT branch. Abort here instead of booting a node that cannot
|
|
1145
1116
|
// store, configure or probe itself and whose manifest then makes every hub
|
|
1146
1117
|
// routing decision about it wrong. See `infra-boot-guard.ts`.
|
|
1147
|
-
|
|
1118
|
+
assertRequiredInfraResolved(resolutions, config.addonsDir);
|
|
1148
1119
|
}
|
|
1149
1120
|
// ---------------------------------------------------------------------------
|
|
1150
1121
|
// Phase 1.5: Load cluster-capable addon packages
|
|
@@ -1156,7 +1127,7 @@ async function loadClusterCapableAddons(broker, config, capabilityRegistry, load
|
|
|
1156
1127
|
// the single-copy collapse removes. The closure dir is deliberately NOT added:
|
|
1157
1128
|
// adding it would newly expose `hub-forwarder` / `platform-probe` (the only
|
|
1158
1129
|
// builtins carrying `execution`) as spawn candidates.
|
|
1159
|
-
const addonPackageDirs =
|
|
1130
|
+
const addonPackageDirs = resolveAddonPackageDirs(config.addonsDir, systemSkipList());
|
|
1160
1131
|
if (addonPackageDirs.length === 0)
|
|
1161
1132
|
return;
|
|
1162
1133
|
// ── Phase 0 (cross-package): collect every group-eligible addon
|
|
@@ -1170,12 +1141,12 @@ async function loadClusterCapableAddons(broker, config, capabilityRegistry, load
|
|
|
1170
1141
|
// so each dir is parsed once.
|
|
1171
1142
|
const dirToLoader = new Map();
|
|
1172
1143
|
for (const dir of addonPackageDirs) {
|
|
1173
|
-
const loader = new
|
|
1144
|
+
const loader = new AddonLoader();
|
|
1174
1145
|
try {
|
|
1175
1146
|
await loader.loadFromAddonDir(dir);
|
|
1176
1147
|
}
|
|
1177
1148
|
catch (err) {
|
|
1178
|
-
console.warn(`[Agent] Skipping ${dir}: ${
|
|
1149
|
+
console.warn(`[Agent] Skipping ${dir}: ${errMsg(err)}`);
|
|
1179
1150
|
continue;
|
|
1180
1151
|
}
|
|
1181
1152
|
dirToLoader.set(dir, loader);
|
|
@@ -1184,11 +1155,11 @@ async function loadClusterCapableAddons(broker, config, capabilityRegistry, load
|
|
|
1184
1155
|
continue;
|
|
1185
1156
|
if (registered.declaration.execution === undefined)
|
|
1186
1157
|
continue;
|
|
1187
|
-
const placement =
|
|
1158
|
+
const placement = resolveAddonPlacement(registered.declaration);
|
|
1188
1159
|
if (placement === 'hub-only')
|
|
1189
1160
|
continue;
|
|
1190
1161
|
allGroupCandidates.push({
|
|
1191
|
-
groupId:
|
|
1162
|
+
groupId: resolveRunnerId(registered.declaration, registered.declaration.id),
|
|
1192
1163
|
addonId: registered.declaration.id,
|
|
1193
1164
|
addonDir: dir,
|
|
1194
1165
|
version: registered.declaration.version ?? '0.0.0',
|
|
@@ -1214,7 +1185,7 @@ async function loadClusterCapableAddons(broker, config, capabilityRegistry, load
|
|
|
1214
1185
|
// Shared with the reload path (`ensureGroupRunner`) so the boot and
|
|
1215
1186
|
// reload registrations can never drift — the drift was the in-process
|
|
1216
1187
|
// reload-wedge's root cause.
|
|
1217
|
-
|
|
1188
|
+
registerGroupRunnerProviders({ broker, capabilityRegistry, loadedAddons }, addons);
|
|
1218
1189
|
console.log(`[Agent] Group "${groupId}" spawned with ${addons.length} addon(s): ${addons.map((a) => a.addonId).join(', ')}`);
|
|
1219
1190
|
}
|
|
1220
1191
|
catch (err) {
|
|
@@ -1236,7 +1207,7 @@ async function loadClusterCapableAddons(broker, config, capabilityRegistry, load
|
|
|
1236
1207
|
const addonId = registered.declaration.id;
|
|
1237
1208
|
if (loadedAddons.has(addonId))
|
|
1238
1209
|
continue;
|
|
1239
|
-
if (!
|
|
1210
|
+
if (!isDeployableToAgent(registered.declaration))
|
|
1240
1211
|
continue;
|
|
1241
1212
|
console.warn(`[Agent] Addon "${addonId}" is deployable but missing from any spawned group — verify package.json execution field`);
|
|
1242
1213
|
}
|
|
@@ -1256,25 +1227,25 @@ async function loadDeployedAddons(broker, addonsDir, dataDir, loadedAddons, stor
|
|
|
1256
1227
|
// (re)dispatched to a subprocess, which loads the real module itself.
|
|
1257
1228
|
// Same skip: a `$agent.deploy` of `@camstack/system` still lands bytes in the
|
|
1258
1229
|
// addon root, but a reload must not adopt them over the closure.
|
|
1259
|
-
const packageDirs =
|
|
1230
|
+
const packageDirs = resolveAddonPackageDirs(addonsDir, systemSkipList());
|
|
1260
1231
|
const groupCandidates = [];
|
|
1261
1232
|
// Dirs holding a deployable NON-group addon — and ONLY these — need the
|
|
1262
1233
|
// importing loader. None exist under the default `hub-only` placement
|
|
1263
1234
|
// (deployable ⟹ cluster-capable), so the heavy import path is never taken.
|
|
1264
1235
|
const inProcessDirs = new Set();
|
|
1265
1236
|
for (const dir of packageDirs) {
|
|
1266
|
-
const manifest =
|
|
1237
|
+
const manifest = readPackageManifestAddons(dir);
|
|
1267
1238
|
if (!manifest)
|
|
1268
1239
|
continue;
|
|
1269
1240
|
for (const decl of manifest.declarations) {
|
|
1270
1241
|
const addonId = decl.id;
|
|
1271
1242
|
if (loadedAddons.has(addonId))
|
|
1272
1243
|
continue;
|
|
1273
|
-
if (!
|
|
1244
|
+
if (!isDeployableToAgent(decl))
|
|
1274
1245
|
continue;
|
|
1275
|
-
if (
|
|
1246
|
+
if (isGroupRunnerAddon(decl)) {
|
|
1276
1247
|
groupCandidates.push({
|
|
1277
|
-
groupId:
|
|
1248
|
+
groupId: resolveRunnerId(decl, addonId),
|
|
1278
1249
|
addonId,
|
|
1279
1250
|
addonDir: dir,
|
|
1280
1251
|
version: decl.version ?? '0.0.0',
|
|
@@ -1309,7 +1280,7 @@ async function loadDeployedAddons(broker, addonsDir, dataDir, loadedAddons, stor
|
|
|
1309
1280
|
}
|
|
1310
1281
|
const deployLogger = loggerFactory('agent-group-runner');
|
|
1311
1282
|
for (const [groupId, addons] of grouped) {
|
|
1312
|
-
await
|
|
1283
|
+
await ensureGroupRunner({ broker, capabilityRegistry, loadedAddons, logger: deployLogger }, groupId, addons);
|
|
1313
1284
|
}
|
|
1314
1285
|
}
|
|
1315
1286
|
/**
|
|
@@ -1331,28 +1302,28 @@ async function loadInProcessDeployedAddons(broker, dirs, dataDir, loadedAddons,
|
|
|
1331
1302
|
capabilityRegistry,
|
|
1332
1303
|
};
|
|
1333
1304
|
for (const dir of dirs) {
|
|
1334
|
-
const loader = new
|
|
1305
|
+
const loader = new AddonLoader();
|
|
1335
1306
|
try {
|
|
1336
1307
|
await loader.loadFromAddonDir(dir);
|
|
1337
1308
|
}
|
|
1338
1309
|
catch (err) {
|
|
1339
|
-
console.warn(`[Agent] Skipping ${dir}: ${
|
|
1310
|
+
console.warn(`[Agent] Skipping ${dir}: ${errMsg(err)}`);
|
|
1340
1311
|
continue;
|
|
1341
1312
|
}
|
|
1342
1313
|
for (const registered of loader.listAddons()) {
|
|
1343
1314
|
const addonId = registered.declaration.id;
|
|
1344
1315
|
if (loadedAddons.has(addonId))
|
|
1345
1316
|
continue;
|
|
1346
|
-
if (!
|
|
1317
|
+
if (!isDeployableToAgent(registered.declaration))
|
|
1347
1318
|
continue;
|
|
1348
1319
|
// Group-runner addons are dispatched to subprocesses by the caller.
|
|
1349
|
-
if (
|
|
1320
|
+
if (isGroupRunnerAddon(registered.declaration))
|
|
1350
1321
|
continue;
|
|
1351
1322
|
try {
|
|
1352
1323
|
const instance = new registered.addonClass();
|
|
1353
|
-
const context = await
|
|
1324
|
+
const context = await createAddonContext(broker, registered.declaration, dataDir, contextOptions);
|
|
1354
1325
|
await instance.initialize(context);
|
|
1355
|
-
const serviceSchema =
|
|
1326
|
+
const serviceSchema = createAddonService(instance, registered.declaration);
|
|
1356
1327
|
broker.createService(serviceSchema);
|
|
1357
1328
|
loadedAddons.set(addonId, {
|
|
1358
1329
|
id: addonId,
|
|
@@ -1365,7 +1336,7 @@ async function loadInProcessDeployedAddons(broker, dirs, dataDir, loadedAddons,
|
|
|
1365
1336
|
console.log(`[Agent] Deployed addon "${addonId}" loaded in-process`);
|
|
1366
1337
|
}
|
|
1367
1338
|
catch (err) {
|
|
1368
|
-
const msg =
|
|
1339
|
+
const msg = errMsg(err);
|
|
1369
1340
|
console.error(`[Agent] Failed to load deployed addon "${addonId}": ${msg}`);
|
|
1370
1341
|
loadedAddons.set(addonId, { id: addonId, status: 'error' });
|
|
1371
1342
|
}
|
|
@@ -1407,7 +1378,7 @@ function readAgentVersion() {
|
|
|
1407
1378
|
* copy is authoritative.
|
|
1408
1379
|
*/
|
|
1409
1380
|
function systemSkipList() {
|
|
1410
|
-
return
|
|
1381
|
+
return resolveHubClosurePackageDir(BUILTINS_PACKAGE) === null ? [] : [BUILTINS_PACKAGE];
|
|
1411
1382
|
}
|
|
1412
1383
|
// ---------------------------------------------------------------------------
|
|
1413
1384
|
// E1 helper — agent-local UDS child manifest adaptation
|
|
@@ -1444,3 +1415,4 @@ function buildAgentChildUdsManifest(nodeId, childId, caps) {
|
|
|
1444
1415
|
const addons = [...capsByAddon.entries()].map(([addonId, capNames]) => ({ addonId, capabilities: [...capNames] }));
|
|
1445
1416
|
return { nodeId, addons };
|
|
1446
1417
|
}
|
|
1418
|
+
export { startAgent };
|