@camstack/server 1.2.593 → 1.2.594
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon-root-inventory.js +11 -50
- package/dist/agent/addon-scan.js +6 -44
- package/dist/agent/agent-addon-runtime.js +3 -8
- package/dist/agent/agent-addon-updates.js +12 -52
- package/dist/agent/agent-cap-dispatch-service.js +3 -5
- package/dist/agent/agent-config.js +7 -42
- package/dist/agent/agent-deploy-swap.js +5 -41
- package/dist/agent/agent-group-runner.js +8 -47
- package/dist/agent/agent-http-auth.js +10 -22
- package/dist/agent/agent-http.js +69 -115
- package/dist/agent/agent-hub-addon-update.js +10 -16
- package/dist/agent/agent-hub-registration.js +4 -7
- package/dist/agent/agent-infra-capabilities.js +5 -9
- package/dist/agent/agent-json-file.js +5 -42
- package/dist/agent/agent-mesh-gate.js +5 -8
- package/dist/agent/agent-node-certificates.js +25 -28
- package/dist/agent/agent-pairing-local.js +10 -47
- package/dist/agent/agent-pairing-routes.js +16 -20
- package/dist/agent/agent-pairing-store.js +18 -58
- package/dist/agent/agent-reconnect.js +1 -4
- package/dist/agent/agent-service.js +42 -78
- package/dist/agent/agent-sniffing-listener.js +11 -48
- package/dist/agent/agent-undeploy.js +5 -41
- package/dist/agent/agent-update-routes.js +14 -19
- package/dist/agent/agent-update-service.js +38 -80
- package/dist/agent/apply-model-distribution.js +1 -4
- package/dist/agent/apply-stage-dev-uploads.js +6 -9
- package/dist/agent/derive-hub-url.js +3 -8
- package/dist/agent/fetch-bundle-from-hub.js +6 -9
- package/dist/agent/hubs-found.js +6 -43
- package/dist/agent/infra-boot-guard.js +1 -4
- package/dist/agent/infra-boot-plan.js +2 -6
- package/dist/agent/main.js +158 -186
- package/dist/agent/register-agent-cap-dispatch.js +3 -6
- package/dist/agent/serial-queue.js +2 -7
- package/dist/agent-status-page.js +1 -4
- package/dist/api/addon-upload.js +22 -61
- package/dist/api/agent-addon-delivery.js +6 -11
- package/dist/api/auth-whoami.js +1 -4
- package/dist/api/backup-download-pump.js +5 -9
- package/dist/api/backup-download.js +7 -10
- package/dist/api/core/addon-settings.router.js +16 -19
- package/dist/api/core/agent-pairing.router.js +76 -79
- package/dist/api/core/agents.router.js +21 -24
- package/dist/api/core/auth-admin-tokens.procedures.js +48 -52
- package/dist/api/core/auth-device-cert.procedures.js +62 -67
- package/dist/api/core/auth-limits.js +16 -25
- package/dist/api/core/auth-reauth.procedures.js +32 -37
- package/dist/api/core/auth-revoke-credentials.procedures.js +22 -26
- package/dist/api/core/auth-sessions.procedures.js +52 -56
- package/dist/api/core/auth.router.js +300 -304
- package/dist/api/core/cap-providers.js +74 -121
- package/dist/api/core/capabilities.router.js +34 -37
- package/dist/api/core/capability-preference-writes.js +10 -17
- package/dist/api/core/certificates.router.js +106 -109
- package/dist/api/core/cluster-nodes.router.js +15 -18
- package/dist/api/core/collection-preference.js +2 -6
- package/dist/api/core/event-bus-proxy.router.js +34 -37
- package/dist/api/core/failure-contributions.js +4 -8
- package/dist/api/core/hwaccel.router.js +17 -20
- package/dist/api/core/integration-secrets.js +8 -12
- package/dist/api/core/lifecycle-job-runner.js +13 -49
- package/dist/api/core/live-events.router.js +29 -32
- package/dist/api/core/load-contributions.js +4 -8
- package/dist/api/core/logging-settings.js +30 -43
- package/dist/api/core/logs.router.js +40 -43
- package/dist/api/core/notifications.router.js +15 -18
- package/dist/api/core/public-event-filter.js +4 -8
- package/dist/api/core/repl.router.js +18 -21
- package/dist/api/core/request-census-settings.js +11 -16
- package/dist/api/core/server-management.provider.js +1 -4
- package/dist/api/core/server-name.js +9 -17
- package/dist/api/core/site-location.js +16 -24
- package/dist/api/core/stream-probe.router.js +21 -24
- package/dist/api/core/system-events.router.js +23 -26
- package/dist/api/cors/cors-policy.js +20 -29
- package/dist/api/cors/register-cors-policy.js +9 -14
- package/dist/api/deploy-stage-registry.js +6 -10
- package/dist/api/dev-uploads-bundle.js +12 -50
- package/dist/api/health/adaptive-probe.routes.js +9 -13
- package/dist/api/health/health.routes.js +8 -13
- package/dist/api/hub-cluster-url.js +3 -9
- package/dist/api/model-distributor.js +12 -50
- package/dist/api/oauth2/consent-page.js +10 -13
- package/dist/api/oauth2/oauth2-routes.js +17 -25
- package/dist/api/oauth2/private-host.js +1 -4
- package/dist/api/server-upload-propagate.js +15 -51
- package/dist/api/server-upload.js +13 -50
- package/dist/api/sse-no-compression.js +1 -4
- package/dist/api/static/frame-policy.js +4 -9
- package/dist/api/static/precompressed-asset.js +15 -57
- package/dist/api/static/spa-mount.js +8 -47
- package/dist/api/static/spa-static.js +5 -12
- package/dist/api/tarball-manifest.js +7 -44
- package/dist/api/trpc/cap-mount-helpers.js +7 -12
- package/dist/api/trpc/cap-route-error-formatter.js +10 -13
- package/dist/api/trpc/cap-router-runtime.js +40 -51
- package/dist/api/trpc/client-ip.js +9 -20
- package/dist/api/trpc/core-cap-bridge.js +32 -42
- package/dist/api/trpc/device-collection-dispatch.js +23 -27
- package/dist/api/trpc/device-config-secret-redaction.js +24 -40
- package/dist/api/trpc/generated-cap-mounts.js +218 -222
- package/dist/api/trpc/generated-cap-routers.js +4688 -4850
- package/dist/api/trpc/http-request-census-hook.js +8 -11
- package/dist/api/trpc/http-request-census.js +34 -41
- package/dist/api/trpc/http-request-census.singleton.js +4 -8
- package/dist/api/trpc/localized-schema-paths.js +1 -4
- package/dist/api/trpc/principal-visibility.js +14 -22
- package/dist/api/trpc/remote-addon-update-grant.js +1 -4
- package/dist/api/trpc/scope-access.js +10 -15
- package/dist/api/trpc/server-provided-caps.js +3 -7
- package/dist/api/trpc/share-view-access.js +6 -14
- package/dist/api/trpc/trpc-error-device-tags.js +3 -6
- package/dist/api/trpc/trpc-error-principal.js +2 -6
- package/dist/api/trpc/trpc.context.js +20 -25
- package/dist/api/trpc/trpc.middleware.js +38 -46
- package/dist/api/trpc/trpc.router.js +58 -99
- package/dist/api/trpc/user-error.js +5 -8
- package/dist/api/trpc/ws-request-census.js +17 -24
- package/dist/api/trpc/ws-slow-consumer-guard.js +11 -16
- package/dist/api/upload-auth.js +5 -9
- package/dist/auth/addon-route-jwt-gate.js +4 -7
- package/dist/auth/addon-route-share-gate.js +7 -12
- package/dist/auth/app-confirm-page.js +17 -22
- package/dist/auth/app-confirm-routes.js +26 -31
- package/dist/auth/app-confirmations.js +7 -44
- package/dist/auth/app-page-shell.js +17 -30
- package/dist/auth/app-page-strings.js +11 -17
- package/dist/auth/app-passkey-page.js +16 -21
- package/dist/auth/app-passkey-routes.js +37 -41
- package/dist/auth/app-refused-route.js +13 -18
- package/dist/auth/auth-network-policy.js +11 -15
- package/dist/auth/auth-provider-gate.js +4 -9
- package/dist/auth/auth-rate-limit.js +3 -7
- package/dist/auth/client-address.js +6 -9
- package/dist/auth/device-enroll-proofs.js +6 -43
- package/dist/auth/external-link-intents.js +3 -7
- package/dist/auth/hub-sessions.js +6 -12
- package/dist/auth/native-link-completions.js +7 -11
- package/dist/auth/native-return.js +9 -13
- package/dist/auth/passkey-provider.js +26 -32
- package/dist/auth/second-factors.js +6 -10
- package/dist/auth/session-connections.js +6 -12
- package/dist/auth/session-cookie.js +12 -23
- package/dist/auth/session-lifetimes.js +8 -12
- package/dist/auth/session-mint.js +4 -11
- package/dist/auth/sso-finish-app.js +13 -16
- package/dist/auth/sso-finish-deps.js +21 -27
- package/dist/auth/sso-finish.js +28 -38
- package/dist/auth/sso-routes.js +22 -25
- package/dist/auth/used-bridge-ids.js +1 -5
- package/dist/boot/boot-config.js +16 -55
- package/dist/boot/init-sequence.js +1 -4
- package/dist/boot/integration-id-backfill.js +3 -8
- package/dist/boot/platform-keychain-key.js +5 -42
- package/dist/boot/post-boot.service.js +14 -18
- package/dist/boot/reconcile-lifecycle-jobs.js +3 -6
- package/dist/boot/retired-public-url-env.js +4 -8
- package/dist/bootstrap-packages.js +1 -4
- package/dist/closure-node-modules.js +2 -38
- package/dist/closure-patches/patch-reach.json +6 -2
- package/dist/core/addon/addon-call-gateway.js +1 -5
- package/dist/core/addon/addon-package.service.js +83 -123
- package/dist/core/addon/addon-process-view.js +2 -6
- package/dist/core/addon/addon-realign-plan.js +1 -4
- package/dist/core/addon/addon-registry.service.js +199 -185
- package/dist/core/addon/addon-row-manifest.js +1 -4
- package/dist/core/addon/addon-search.service.js +3 -7
- package/dist/core/addon/addon-settings-provider.js +1 -3
- package/dist/core/addon/addon-uninstall.js +4 -11
- package/dist/core/addon/addon.tokens.js +2 -5
- package/dist/core/addon/device-meta-mirror.js +8 -13
- package/dist/core/addon/forked-custom-actions.js +6 -11
- package/dist/core/addon/integration-visibility.js +3 -6
- package/dist/core/addon/materialize-route-path.js +1 -4
- package/dist/core/addon/package-dir-utils.js +8 -45
- package/dist/core/addon/prune-misplaced-addons.js +5 -43
- package/dist/core/addon/runner-convergence.js +1 -4
- package/dist/core/addon/runner-serialization.js +1 -4
- package/dist/core/addon/runner-spawn-fanout.js +3 -8
- package/dist/core/addon/settings-door-wiring.js +5 -8
- package/dist/core/addon/single-flight-refresh.js +1 -4
- package/dist/core/addon-bridge/addon-bridge.service.js +7 -44
- package/dist/core/addon-pages/addon-pages.service.js +3 -40
- package/dist/core/addon-widgets/addon-widgets.service.js +4 -42
- package/dist/core/agent/agent-addon-backfill.js +9 -17
- package/dist/core/agent/agent-delivery-ledger.js +3 -7
- package/dist/core/agent/agent-liveness-event.js +2 -6
- package/dist/core/agent/agent-pairing-http.js +41 -45
- package/dist/core/agent/agent-registry.service.js +53 -92
- package/dist/core/agent/cluster-node-history-mirror.js +3 -7
- package/dist/core/agent/cluster-node-history-store.js +113 -58
- package/dist/core/agent/hub-agent-pairing.js +1 -5
- package/dist/core/agent/node-certificates-client.js +3 -6
- package/dist/core/agent/node-directory.js +1 -4
- package/dist/core/app-links/well-known-app-links.js +2 -6
- package/dist/core/auth/auth.service.js +3 -8
- package/dist/core/auth/handoff-code.service.js +11 -49
- package/dist/core/auth/record-kinds.js +35 -0
- package/dist/core/auth/share-token.service.js +174 -122
- package/dist/core/capability/capability.service.js +1 -5
- package/dist/core/config/config.schema.js +1 -6
- package/dist/core/config/config.service.js +2 -6
- package/dist/core/events/event-bus.service.js +3 -7
- package/dist/core/feature/feature.service.js +2 -6
- package/dist/core/lifecycle/lifecycle-runner.singleton.js +5 -10
- package/dist/core/lifecycle/lifecycle-state-machine.js +1 -5
- package/dist/core/logging/log-ring-buffer.js +1 -5
- package/dist/core/logging/logging.service.js +5 -9
- package/dist/core/logging/scoped-logger.js +1 -5
- package/dist/core/moleculer/agent-readiness-pull.js +1 -4
- package/dist/core/moleculer/agent-registration-request.js +3 -7
- package/dist/core/moleculer/cap-call-fn.js +39 -5
- package/dist/core/moleculer/cap-route-authority.js +3 -8
- package/dist/core/moleculer/hub-mesh-guard.js +5 -9
- package/dist/core/moleculer/moleculer.service.js +108 -119
- package/dist/core/network/network-quality.service.js +2 -6
- package/dist/core/notification/notification-wrapper.service.js +3 -7
- package/dist/core/notification/toast-wrapper.service.js +3 -7
- package/dist/core/provider/provider.tokens.js +1 -4
- package/dist/core/repl/repl-engine.service.js +4 -8
- package/dist/core/runner-health/runner-boot-check.js +7 -15
- package/dist/core/runner-health/update-preflight.js +9 -14
- package/dist/core/server-update/server-update.service.js +12 -49
- package/dist/core/server-update/system-apply-closure-patches.js +6 -42
- package/dist/core/server-update/system-ensure-prebuilds.js +3 -6
- package/dist/core/server-update/system-exec-npm.js +2 -5
- package/dist/core/storage/fs-storage-backend.js +1 -5
- package/dist/core/storage/storage-location-manager.js +1 -5
- package/dist/core/storage/storage.service.js +2 -6
- package/dist/core/streaming/ffmpeg-stream-info.js +3 -8
- package/dist/core/streaming/stream-probe.service.js +12 -16
- package/dist/core/streaming/ttl-cache.js +1 -5
- package/dist/core/topology/topology-emitter.service.js +19 -23
- package/dist/core/update-availability-emitter.js +3 -7
- package/dist/core/update-availability-store.js +8 -45
- package/dist/core/updates/agent-installed-packages.js +5 -9
- package/dist/core/updates/restart-marker.js +3 -8
- package/dist/core/updates/update-check-scheduler.js +13 -18
- package/dist/core/updates/wrapper-update-checker.js +3 -7
- package/dist/first-boot-addon-plan.js +2 -6
- package/dist/framework-nodepath.js +2 -38
- package/dist/i18n/request-lang.js +7 -11
- package/dist/launcher.js +30 -65
- package/dist/lifecycle-journal-path.js +2 -38
- package/dist/main.js +287 -324
- package/dist/manual-boot.js +120 -157
- package/dist/node-role.js +1 -4
- package/dist/package-inventory.js +7 -48
- package/dist/pending-restore.js +7 -43
- package/dist/single-copy-cleanup-runner.js +9 -45
- package/dist/single-copy-cleanup.js +7 -15
- package/dist/sqlite-restore.js +9 -48
- package/dist/starter.js +4 -39
- package/dist/ui-serving/lazy-ui-serving.js +3 -39
- package/package.json +13 -13
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Agent HTTP auth — pure request-authorization helpers for the agent's
|
|
4
3
|
* port-4444 API (mirrors the hub's /health hardening).
|
|
@@ -15,20 +14,9 @@
|
|
|
15
14
|
* agent made up, a proof the mesh CA signed), never by who calls. An
|
|
16
15
|
* unpaired agent offers nothing else remotely.
|
|
17
16
|
*/
|
|
18
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.isLocalHostHeader = isLocalHostHeader;
|
|
20
|
-
exports.authInputOf = authInputOf;
|
|
21
|
-
exports.hasForwardingHeader = hasForwardingHeader;
|
|
22
|
-
exports.isGenuineLoopback = isGenuineLoopback;
|
|
23
|
-
exports.isLoopbackAddress = isLoopbackAddress;
|
|
24
|
-
exports.readBearerToken = readBearerToken;
|
|
25
|
-
exports.isAuthorizedAgentRequest = isAuthorizedAgentRequest;
|
|
26
|
-
exports.isPairingProtocolRequest = isPairingProtocolRequest;
|
|
27
|
-
exports.isUnlockSessionRequest = isUnlockSessionRequest;
|
|
28
|
-
exports.isUnlockInfoRequest = isUnlockInfoRequest;
|
|
29
17
|
const LOCAL_HOSTNAMES = new Set(['localhost', '127.0.0.1', '[::1]']);
|
|
30
18
|
/** `host[:port]` of the agent's own machine: localhost, 127.0.0.1 or [::1]. */
|
|
31
|
-
function isLocalHostHeader(host) {
|
|
19
|
+
export function isLocalHostHeader(host) {
|
|
32
20
|
const lower = host.trim().toLowerCase();
|
|
33
21
|
const name = lower.startsWith('[') ? lower.slice(0, lower.indexOf(']') + 1) : lower.split(':')[0];
|
|
34
22
|
return name !== undefined && LOCAL_HOSTNAMES.has(name);
|
|
@@ -65,7 +53,7 @@ function isBrowserSafeLocal(input) {
|
|
|
65
53
|
return true;
|
|
66
54
|
}
|
|
67
55
|
/** Build the auth input of a Node/Fastify request. */
|
|
68
|
-
function authInputOf(req) {
|
|
56
|
+
export function authInputOf(req) {
|
|
69
57
|
const one = (name) => {
|
|
70
58
|
const v = req.headers[name];
|
|
71
59
|
return typeof v === 'string' ? v : undefined;
|
|
@@ -91,18 +79,18 @@ const FORWARDING_HEADERS = [
|
|
|
91
79
|
'cf-connecting-ip',
|
|
92
80
|
];
|
|
93
81
|
/** True when any header says the request was relayed (D857): then loopback proves nothing. */
|
|
94
|
-
function hasForwardingHeader(headers) {
|
|
82
|
+
export function hasForwardingHeader(headers) {
|
|
95
83
|
return FORWARDING_HEADERS.some((name) => headers[name] !== undefined);
|
|
96
84
|
}
|
|
97
85
|
/**
|
|
98
86
|
* A request from this machine itself: a loopback peer AND no forwarding header. A reverse proxy on
|
|
99
87
|
* the same host makes every caller look like 127.0.0.1; the header it adds is what gives it away.
|
|
100
88
|
*/
|
|
101
|
-
function isGenuineLoopback(input) {
|
|
89
|
+
export function isGenuineLoopback(input) {
|
|
102
90
|
return (input.forwarded !== true && isLoopbackAddress(input.remoteAddress) && isBrowserSafeLocal(input));
|
|
103
91
|
}
|
|
104
92
|
/** IPv4/IPv6 loopback (incl. the IPv4-mapped IPv6 form Node reports). */
|
|
105
|
-
function isLoopbackAddress(addr) {
|
|
93
|
+
export function isLoopbackAddress(addr) {
|
|
106
94
|
if (!addr)
|
|
107
95
|
return false;
|
|
108
96
|
if (addr === '::1')
|
|
@@ -115,7 +103,7 @@ function isLoopbackAddress(addr) {
|
|
|
115
103
|
return parts.every((p) => /^\d{1,3}$/.test(p));
|
|
116
104
|
}
|
|
117
105
|
/** Extract the bearer token value, or `null`. */
|
|
118
|
-
function readBearerToken(authorization) {
|
|
106
|
+
export function readBearerToken(authorization) {
|
|
119
107
|
if (!authorization?.startsWith('Bearer '))
|
|
120
108
|
return null;
|
|
121
109
|
const token = authorization.slice('Bearer '.length);
|
|
@@ -125,7 +113,7 @@ function readBearerToken(authorization) {
|
|
|
125
113
|
* True when the request may access the protected agent API: it comes from this
|
|
126
114
|
* machine, or it carries a session token minted from a hub unlock ticket.
|
|
127
115
|
*/
|
|
128
|
-
function isAuthorizedAgentRequest(input, sessionTokenOk = () => false) {
|
|
116
|
+
export function isAuthorizedAgentRequest(input, sessionTokenOk = () => false) {
|
|
129
117
|
if (isGenuineLoopback(input))
|
|
130
118
|
return true;
|
|
131
119
|
const token = readBearerToken(input.authorization);
|
|
@@ -150,7 +138,7 @@ const PAIRING_POST = new Set([
|
|
|
150
138
|
* the network without a session. `forget` is NOT among them — leaving a hub is an
|
|
151
139
|
* authenticated action (this machine, or a hub-unlocked session).
|
|
152
140
|
*/
|
|
153
|
-
function isPairingProtocolRequest(method, urlPath) {
|
|
141
|
+
export function isPairingProtocolRequest(method, urlPath) {
|
|
154
142
|
const pathOnly = urlPath.split('?')[0] ?? urlPath;
|
|
155
143
|
if (method === 'GET')
|
|
156
144
|
return PAIRING_GET.has(pathOnly);
|
|
@@ -162,7 +150,7 @@ function isPairingProtocolRequest(method, urlPath) {
|
|
|
162
150
|
* Redeem a hub unlock ticket for a session token — reachable remotely without
|
|
163
151
|
* a prior session (the ticket IS the proof). Paired agents only.
|
|
164
152
|
*/
|
|
165
|
-
function isUnlockSessionRequest(method, urlPath) {
|
|
153
|
+
export function isUnlockSessionRequest(method, urlPath) {
|
|
166
154
|
const pathOnly = urlPath.split('?')[0] ?? urlPath;
|
|
167
155
|
return method === 'POST' && pathOnly === '/api/agent/session';
|
|
168
156
|
}
|
|
@@ -170,7 +158,7 @@ function isUnlockSessionRequest(method, urlPath) {
|
|
|
170
158
|
* Public unlock probe: `nodeId` + hub HTTPS origin for Continue-with-hub (D821).
|
|
171
159
|
* No secrets, no metrics — only what the locked unlock screen needs.
|
|
172
160
|
*/
|
|
173
|
-
function isUnlockInfoRequest(method, urlPath) {
|
|
161
|
+
export function isUnlockInfoRequest(method, urlPath) {
|
|
174
162
|
const pathOnly = urlPath.split('?')[0] ?? urlPath;
|
|
175
163
|
return method === 'GET' && pathOnly === '/api/agent/unlock-info';
|
|
176
164
|
}
|
package/dist/agent/agent-http.js
CHANGED
|
@@ -1,82 +1,30 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Agent HTTP server -- lightweight Fastify instance for agent status,
|
|
4
3
|
* process management, config editing, and static UI serving.
|
|
5
4
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
});
|
|
22
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
-
var ownKeys = function(o) {
|
|
24
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
-
var ar = [];
|
|
26
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
-
return ar;
|
|
28
|
-
};
|
|
29
|
-
return ownKeys(o);
|
|
30
|
-
};
|
|
31
|
-
return function (mod) {
|
|
32
|
-
if (mod && mod.__esModule) return mod;
|
|
33
|
-
var result = {};
|
|
34
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
-
__setModuleDefault(result, mod);
|
|
36
|
-
return result;
|
|
37
|
-
};
|
|
38
|
-
})();
|
|
39
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
40
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
41
|
-
};
|
|
42
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
-
exports.pickIpv4 = void 0;
|
|
44
|
-
exports.describePairedHub = describePairedHub;
|
|
45
|
-
exports.compareVersions = compareVersions;
|
|
46
|
-
exports.readUiDistCandidate = readUiDistCandidate;
|
|
47
|
-
exports.pickUiDist = pickUiDist;
|
|
48
|
-
exports.resolveUiDistDir = resolveUiDistDir;
|
|
49
|
-
exports.getRegistryNodes = getRegistryNodes;
|
|
50
|
-
exports.resolveHubEndpoint = resolveHubEndpoint;
|
|
51
|
-
exports.isOpaqueHostname = isOpaqueHostname;
|
|
52
|
-
exports.nodeDisplayName = nodeDisplayName;
|
|
53
|
-
exports.mapDiscoveredNodes = mapDiscoveredNodes;
|
|
54
|
-
exports.createAgentHttpServer = createAgentHttpServer;
|
|
55
|
-
exports.startAgentHttpServer = startAgentHttpServer;
|
|
56
|
-
const node_crypto_1 = require("node:crypto");
|
|
57
|
-
const fs = __importStar(require("node:fs"));
|
|
58
|
-
const path = __importStar(require("node:path"));
|
|
59
|
-
const fastify_1 = __importDefault(require("fastify"));
|
|
60
|
-
const zod_1 = require("zod");
|
|
61
|
-
const system_1 = require("@camstack/system");
|
|
62
|
-
const agent_sniffing_listener_js_1 = require("./agent-sniffing-listener.js");
|
|
63
|
-
const agent_http_auth_js_1 = require("./agent-http-auth.js");
|
|
64
|
-
const agent_pairing_routes_js_1 = require("./agent-pairing-routes.js");
|
|
65
|
-
const agent_pairing_store_js_1 = require("./agent-pairing-store.js");
|
|
66
|
-
const node_1 = require("@camstack/types/node");
|
|
67
|
-
const agent_update_routes_js_1 = require("./agent-update-routes.js");
|
|
68
|
-
const derive_hub_url_js_1 = require("./derive-hub-url.js");
|
|
69
|
-
Object.defineProperty(exports, "pickIpv4", { enumerable: true, get: function () { return derive_hub_url_js_1.pickIpv4; } });
|
|
70
|
-
const agent_addon_runtime_js_1 = require("./agent-addon-runtime.js");
|
|
71
|
-
const hubs_found_js_1 = require("./hubs-found.js");
|
|
5
|
+
import { randomBytes } from 'node:crypto';
|
|
6
|
+
import * as fs from 'node:fs';
|
|
7
|
+
import * as path from 'node:path';
|
|
8
|
+
import Fastify from 'fastify';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { caFingerprintCompact, loadAgentHttps, UnlockTicketJtiStore, verifyAgentUiUnlockTicket, } from '@camstack/system';
|
|
11
|
+
import { startSniffingListener } from './agent-sniffing-listener.js';
|
|
12
|
+
import { isAuthorizedAgentRequest, authInputOf, isPairingProtocolRequest, isUnlockInfoRequest, isUnlockSessionRequest, } from './agent-http-auth.js';
|
|
13
|
+
import { registerAgentPairingRoutes } from './agent-pairing-routes.js';
|
|
14
|
+
import { isAgentPaired, pinnedMeshCaCert } from './agent-pairing-store.js';
|
|
15
|
+
import { writeOwnerOnlyFileSync } from '@camstack/types/node';
|
|
16
|
+
import { registerAgentUpdateRoutes } from './agent-update-routes.js';
|
|
17
|
+
import { deriveHubUrlFromHubAddress, pickIpv4 } from './derive-hub-url.js';
|
|
18
|
+
import { addonsFromStatus, joinAddonRuntime, processesFromList } from './agent-addon-runtime.js';
|
|
19
|
+
import { createHubsFinder } from './hubs-found.js';
|
|
72
20
|
/** The pinned CA's fingerprint, or `null` when none is pinned or the PEM cannot be read. */
|
|
73
|
-
function describePairedHub(pinnedCa, hubHttpsOrigin) {
|
|
21
|
+
export function describePairedHub(pinnedCa, hubHttpsOrigin) {
|
|
74
22
|
if (pinnedCa === null)
|
|
75
23
|
return null;
|
|
76
24
|
try {
|
|
77
25
|
return {
|
|
78
26
|
url: hubHttpsOrigin,
|
|
79
|
-
caFingerprint:
|
|
27
|
+
caFingerprint: caFingerprintCompact(pinnedCa),
|
|
80
28
|
};
|
|
81
29
|
}
|
|
82
30
|
catch {
|
|
@@ -84,7 +32,7 @@ function describePairedHub(pinnedCa, hubHttpsOrigin) {
|
|
|
84
32
|
}
|
|
85
33
|
}
|
|
86
34
|
/** Dotted-numeric version compare; `null` sorts lowest. */
|
|
87
|
-
function compareVersions(a, b) {
|
|
35
|
+
export function compareVersions(a, b) {
|
|
88
36
|
if (a === null && b === null)
|
|
89
37
|
return 0;
|
|
90
38
|
if (a === null)
|
|
@@ -105,7 +53,7 @@ function compareVersions(a, b) {
|
|
|
105
53
|
* Read an agent-ui package dir (`<base>/package.json` + `<base>/dist/index.html`)
|
|
106
54
|
* into a candidate. Returns `null` when the dist is absent/unusable.
|
|
107
55
|
*/
|
|
108
|
-
function readUiDistCandidate(baseDir) {
|
|
56
|
+
export function readUiDistCandidate(baseDir) {
|
|
109
57
|
const dir = path.join(baseDir, 'dist');
|
|
110
58
|
if (!fs.existsSync(path.join(dir, 'index.html')))
|
|
111
59
|
return null;
|
|
@@ -135,13 +83,13 @@ function readUiDistCandidate(baseDir) {
|
|
|
135
83
|
* over `@fastify/static` — so which dir that variable names is load-bearing
|
|
136
84
|
* here, not just at first boot.
|
|
137
85
|
*/
|
|
138
|
-
function pickUiDist(installed, bundled) {
|
|
86
|
+
export function pickUiDist(installed, bundled) {
|
|
139
87
|
if (installed && bundled) {
|
|
140
88
|
return compareVersions(bundled.version, installed.version) > 0 ? bundled.dir : installed.dir;
|
|
141
89
|
}
|
|
142
90
|
return installed?.dir ?? bundled?.dir ?? null;
|
|
143
91
|
}
|
|
144
|
-
function resolveUiDistDir(dataDir,
|
|
92
|
+
export function resolveUiDistDir(dataDir,
|
|
145
93
|
// `CAMSTACK_BUNDLED_ADDONS_DIR` — a dir of package dirs keyed by SHORT name
|
|
146
94
|
// (`<dir>/addon-agent-ui`), the same shape `AddonInstaller.findLocalPackage`
|
|
147
95
|
// joins onto. On a packaged desktop that is the closure's own scope dir,
|
|
@@ -179,42 +127,48 @@ function readConfigFile(configPath) {
|
|
|
179
127
|
}
|
|
180
128
|
/** agent.json holds the agent's private mesh key (D857): owner-only on every OS. */
|
|
181
129
|
function writeConfigFile(configPath, data) {
|
|
182
|
-
|
|
130
|
+
writeOwnerOnlyFileSync(configPath, JSON.stringify(data, null, 2));
|
|
183
131
|
}
|
|
184
132
|
/** Live Moleculer registry snapshot — reused by agent-bootstrap's Option A. */
|
|
185
|
-
function getRegistryNodes(broker) {
|
|
133
|
+
export function getRegistryNodes(broker) {
|
|
186
134
|
try {
|
|
187
|
-
|
|
188
|
-
|
|
135
|
+
return broker.registry.getNodeList({ onlyAvailable: true }).map((node) => ({
|
|
136
|
+
id: node.id,
|
|
137
|
+
...(node.hostname != null ? { hostname: node.hostname } : {}),
|
|
138
|
+
...(node.ipList != null ? { ipList: node.ipList } : {}),
|
|
139
|
+
}));
|
|
189
140
|
}
|
|
190
141
|
catch {
|
|
191
142
|
return [];
|
|
192
143
|
}
|
|
193
144
|
}
|
|
145
|
+
// `pickIpv4` moved to derive-hub-url.ts (single home shared with
|
|
146
|
+
// `deriveHubUrlFromRegistry`); re-exported here to preserve this module's API.
|
|
147
|
+
export { pickIpv4 };
|
|
194
148
|
/**
|
|
195
149
|
* A human-readable, reachable address for the hub node as the agent actually
|
|
196
150
|
* sees it in the Moleculer registry. Used to fill in the effective address
|
|
197
151
|
* even in UDP-discovery mode where no address was manually configured.
|
|
198
152
|
*/
|
|
199
|
-
function resolveHubEndpoint(nodes) {
|
|
153
|
+
export function resolveHubEndpoint(nodes) {
|
|
200
154
|
const hub = nodes.find((n) => n.id === 'hub');
|
|
201
155
|
if (!hub)
|
|
202
156
|
return null;
|
|
203
|
-
return
|
|
157
|
+
return pickIpv4(hub.ipList) ?? hub.hostname ?? null;
|
|
204
158
|
}
|
|
205
159
|
/** A container id (12 hex) or full id (64 hex): a name nobody chose. */
|
|
206
|
-
function isOpaqueHostname(hostname) {
|
|
160
|
+
export function isOpaqueHostname(hostname) {
|
|
207
161
|
return /^[0-9a-f]{12}$|^[0-9a-f]{64}$/.test(hostname);
|
|
208
162
|
}
|
|
209
163
|
/**
|
|
210
164
|
* Prefer a name an operator can recognise over the bare Moleculer node id —
|
|
211
165
|
* and over a container's id, which is what the hostname of a hub in Docker is.
|
|
212
166
|
*/
|
|
213
|
-
function nodeDisplayName(node) {
|
|
167
|
+
export function nodeDisplayName(node) {
|
|
214
168
|
const hostname = typeof node.hostname === 'string' ? node.hostname.trim() : '';
|
|
215
169
|
if (hostname.length > 0 && hostname !== node.id && !isOpaqueHostname(hostname))
|
|
216
170
|
return hostname;
|
|
217
|
-
const ip =
|
|
171
|
+
const ip = pickIpv4(node.ipList);
|
|
218
172
|
if (ip !== null)
|
|
219
173
|
return ip;
|
|
220
174
|
return hostname.length > 0 ? hostname : node.id;
|
|
@@ -223,7 +177,7 @@ function nodeDisplayName(node) {
|
|
|
223
177
|
* Map raw registry nodes (minus self) to the UI-facing shape: the hub carries
|
|
224
178
|
* a human `displayName` (hostname or IPv4) so the UI never shows only `hub`.
|
|
225
179
|
*/
|
|
226
|
-
function mapDiscoveredNodes(nodes, selfId) {
|
|
180
|
+
export function mapDiscoveredNodes(nodes, selfId) {
|
|
227
181
|
return nodes
|
|
228
182
|
.filter((n) => n.id !== selfId)
|
|
229
183
|
.map((n) => {
|
|
@@ -260,7 +214,7 @@ function getEffectiveConfig(configPath, nodeId) {
|
|
|
260
214
|
return {
|
|
261
215
|
name: typeof raw.name === 'string' ? raw.name : nodeId,
|
|
262
216
|
hubAddress: typeof raw.hubAddress === 'string' ? raw.hubAddress : null,
|
|
263
|
-
paired:
|
|
217
|
+
paired: isAgentPaired(configPath),
|
|
264
218
|
hasMeshTls: raw.meshTls !== null && typeof raw.meshTls === 'object',
|
|
265
219
|
};
|
|
266
220
|
}
|
|
@@ -271,18 +225,18 @@ function getEffectiveConfig(configPath, nodeId) {
|
|
|
271
225
|
*/
|
|
272
226
|
const EDITABLE_CONFIG_KEYS = ['name', 'hubAddress', 'logLevel'];
|
|
273
227
|
/** Value types of the editable keys; unknown keys pass through to the allow-list refusal above. */
|
|
274
|
-
const ConfigPatchSchema =
|
|
275
|
-
name:
|
|
276
|
-
hubAddress:
|
|
277
|
-
logLevel:
|
|
228
|
+
const ConfigPatchSchema = z.looseObject({
|
|
229
|
+
name: z.string().max(100).nullable().optional(),
|
|
230
|
+
hubAddress: z.string().max(300).nullable().optional(),
|
|
231
|
+
logLevel: z.enum(['debug', 'info', 'warn', 'error']).optional(),
|
|
278
232
|
});
|
|
279
233
|
/** Clock skew allowed between the hub that issued a ticket and this agent's boot instant. */
|
|
280
234
|
const TICKET_BOOT_SKEW_MS = 2 * 60_000;
|
|
281
235
|
// ---------------------------------------------------------------------------
|
|
282
236
|
// Factory
|
|
283
237
|
// ---------------------------------------------------------------------------
|
|
284
|
-
async function createAgentHttpServer(getBroker, config) {
|
|
285
|
-
const app = (
|
|
238
|
+
export async function createAgentHttpServer(getBroker, config) {
|
|
239
|
+
const app = Fastify({ logger: false });
|
|
286
240
|
// No CORS: the agent's page is served by the agent itself, so every legitimate caller is
|
|
287
241
|
// same-origin. `Access-Control-Allow-Origin: *` would let any web page read this API (D857).
|
|
288
242
|
// -- Auth gate (port-4444 hardening, mirrors the hub's /health work) --
|
|
@@ -297,13 +251,13 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
297
251
|
* ends every session (D857).
|
|
298
252
|
*/
|
|
299
253
|
const unlockSessions = new Map();
|
|
300
|
-
let unlockJtis = new
|
|
254
|
+
let unlockJtis = new UnlockTicketJtiStore();
|
|
301
255
|
let jtiBinding = null;
|
|
302
256
|
const bootMs = Date.now();
|
|
303
257
|
/** What vouches for a session now: the pinned CA, else nothing. */
|
|
304
258
|
const caBinding = () => {
|
|
305
|
-
const ca =
|
|
306
|
-
return ca === null ? null : `ca:${
|
|
259
|
+
const ca = pinnedMeshCaCert(config.configPath);
|
|
260
|
+
return ca === null ? null : `ca:${caFingerprintCompact(ca)}`;
|
|
307
261
|
};
|
|
308
262
|
const sessionOk = (token) => {
|
|
309
263
|
const entry = unlockSessions.get(token);
|
|
@@ -332,19 +286,19 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
332
286
|
const matched = req.routeOptions.url ?? '';
|
|
333
287
|
if (method === 'GET' && matched === '/health')
|
|
334
288
|
return;
|
|
335
|
-
if (
|
|
289
|
+
if (isPairingProtocolRequest(method, matched))
|
|
336
290
|
return;
|
|
337
|
-
if (
|
|
291
|
+
if (isUnlockSessionRequest(method, matched))
|
|
338
292
|
return;
|
|
339
|
-
if (
|
|
293
|
+
if (isUnlockInfoRequest(method, matched))
|
|
340
294
|
return;
|
|
341
|
-
const authInput =
|
|
342
|
-
if (
|
|
295
|
+
const authInput = authInputOf(req);
|
|
296
|
+
if (isAuthorizedAgentRequest(authInput, sessionOk))
|
|
343
297
|
return;
|
|
344
298
|
return reply.status(401).send({ ok: false, error: 'unauthorized' });
|
|
345
299
|
});
|
|
346
300
|
if (config.pairing !== undefined) {
|
|
347
|
-
|
|
301
|
+
registerAgentPairingRoutes(app, {
|
|
348
302
|
pairing: config.pairing,
|
|
349
303
|
onForgetHub: config.onForgetHub ?? (async () => undefined),
|
|
350
304
|
log: (message) => config.log?.info(message),
|
|
@@ -399,15 +353,15 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
399
353
|
// where `eff.hubAddress` is null.
|
|
400
354
|
const resolvedHubAddress = resolveHubEndpoint(nodes);
|
|
401
355
|
// HTTPS origin the agent UI uses for "Continue with hub" unlock (D821).
|
|
402
|
-
const hubHttpsOrigin = (eff.hubAddress !== null ?
|
|
403
|
-
(resolvedHubAddress !== null ?
|
|
356
|
+
const hubHttpsOrigin = (eff.hubAddress !== null ? deriveHubUrlFromHubAddress(eff.hubAddress) : undefined) ??
|
|
357
|
+
(resolvedHubAddress !== null ? deriveHubUrlFromHubAddress(resolvedHubAddress) : undefined) ??
|
|
404
358
|
null;
|
|
405
359
|
// Version of the Electron wrapper (injected by the desktop app); absent
|
|
406
360
|
// when the agent runs headless / in Docker.
|
|
407
361
|
const appVersion = process.env['CAMSTACK_AGENT_APP_VERSION'] ?? null;
|
|
408
362
|
const discoveredNodes = mapDiscoveredNodes(nodes, broker.nodeID);
|
|
409
363
|
// The hub this agent is paired with, for the agent UI's "Paired with" card (D857).
|
|
410
|
-
const pairedHub = describePairedHub(
|
|
364
|
+
const pairedHub = describePairedHub(pinnedMeshCaCert(config.configPath), hubHttpsOrigin);
|
|
411
365
|
try {
|
|
412
366
|
const status = (await broker.call('$agent.status'));
|
|
413
367
|
return {
|
|
@@ -449,8 +403,8 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
449
403
|
const eff = getEffectiveConfig(config.configPath, config.nodeId);
|
|
450
404
|
const nodes = getRegistryNodes(getBroker());
|
|
451
405
|
const resolvedHubAddress = resolveHubEndpoint(nodes);
|
|
452
|
-
const hubHttpsOrigin = (eff.hubAddress !== null ?
|
|
453
|
-
(resolvedHubAddress !== null ?
|
|
406
|
+
const hubHttpsOrigin = (eff.hubAddress !== null ? deriveHubUrlFromHubAddress(eff.hubAddress) : undefined) ??
|
|
407
|
+
(resolvedHubAddress !== null ? deriveHubUrlFromHubAddress(resolvedHubAddress) : undefined) ??
|
|
454
408
|
null;
|
|
455
409
|
return {
|
|
456
410
|
paired: eff.paired,
|
|
@@ -461,7 +415,7 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
461
415
|
// -- Unlock session (hub admin ticket → tab Bearer; D821) -----------
|
|
462
416
|
app.post('/api/agent/session', async (req, reply) => {
|
|
463
417
|
// The ticket is signed by the mesh CA this agent pinned at pairing (D857).
|
|
464
|
-
const pinnedCa =
|
|
418
|
+
const pinnedCa = pinnedMeshCaCert(config.configPath);
|
|
465
419
|
if (pinnedCa === null) {
|
|
466
420
|
return reply.status(400).send({ ok: false, error: 'agent is not paired' });
|
|
467
421
|
}
|
|
@@ -470,7 +424,7 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
470
424
|
return reply.status(400).send({ ok: false, error: 'ticket required' });
|
|
471
425
|
}
|
|
472
426
|
// Nothing issued before this process started is accepted: the one-use ledger is in memory.
|
|
473
|
-
const payload =
|
|
427
|
+
const payload = verifyAgentUiUnlockTicket(pinnedCa, ticket, {
|
|
474
428
|
expectedNodeId: config.nodeId,
|
|
475
429
|
notBeforeMs: bootMs - TICKET_BOOT_SKEW_MS,
|
|
476
430
|
});
|
|
@@ -480,13 +434,13 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
480
434
|
}
|
|
481
435
|
// A new vouching CA or secret starts a new ledger.
|
|
482
436
|
if (jtiBinding !== binding) {
|
|
483
|
-
unlockJtis = new
|
|
437
|
+
unlockJtis = new UnlockTicketJtiStore();
|
|
484
438
|
jtiBinding = binding;
|
|
485
439
|
}
|
|
486
440
|
if (!unlockJtis.consume(payload.jti, payload.exp)) {
|
|
487
441
|
return reply.status(401).send({ ok: false, error: 'unlock ticket already used' });
|
|
488
442
|
}
|
|
489
|
-
const token =
|
|
443
|
+
const token = randomBytes(32).toString('base64url');
|
|
490
444
|
// Session outlives the ticket so the tab keeps working after redeem.
|
|
491
445
|
const sessionExp = Date.now() + 8 * 60 * 60_000;
|
|
492
446
|
unlockSessions.set(token, { exp: sessionExp, binding });
|
|
@@ -508,7 +462,7 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
508
462
|
broker.call('$agent.status').catch(() => null),
|
|
509
463
|
broker.call('$process.list').catch(() => []),
|
|
510
464
|
]);
|
|
511
|
-
return
|
|
465
|
+
return joinAddonRuntime(addonsFromStatus(status), processesFromList(list));
|
|
512
466
|
});
|
|
513
467
|
// -- Addon restart --------------------------------------------------
|
|
514
468
|
app.post('/api/agent/addon/restart', async (req, reply) => {
|
|
@@ -589,13 +543,13 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
589
543
|
// -- Updates (framework local; addon roster read-only) --------------
|
|
590
544
|
// Registered AFTER the onRequest hook above, so every path it mounts is
|
|
591
545
|
// gated by the same loopback-or-bearer rule as the rest of `/api/`.
|
|
592
|
-
|
|
546
|
+
registerAgentUpdateRoutes(app, config.updateControls ?? {});
|
|
593
547
|
// -- Hubs found on this network (D839) ------------------------------
|
|
594
548
|
// The cached list is free; the refresh browses mDNS for a few seconds. The
|
|
595
549
|
// UI asks on demand (opening the edit form, "Search again"), never on its
|
|
596
550
|
// status poll.
|
|
597
551
|
const hubsFinder = config.hubsFinder ??
|
|
598
|
-
|
|
552
|
+
createHubsFinder({
|
|
599
553
|
getConfiguredHubAddress: () => getEffectiveConfig(config.configPath, config.nodeId).hubAddress,
|
|
600
554
|
log: (message) => console.warn(`[Agent] ${message}`),
|
|
601
555
|
});
|
|
@@ -610,7 +564,7 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
610
564
|
// -- Static file serving (agent-ui) ---------------------------------
|
|
611
565
|
const uiDir = resolveUiDistDir(config.dataDir, process.env['CAMSTACK_BUNDLED_ADDONS_DIR']);
|
|
612
566
|
if (uiDir) {
|
|
613
|
-
const fastifyStatic = await
|
|
567
|
+
const fastifyStatic = await import('@fastify/static');
|
|
614
568
|
// Every route the static plugin mounts is the public UI bundle: tag them for the auth gate.
|
|
615
569
|
let mountingAssets = true;
|
|
616
570
|
app.addHook('onRoute', (route) => {
|
|
@@ -653,14 +607,14 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
653
607
|
* of a connection picks the protocol, so there is no second port. Resolves with
|
|
654
608
|
* the listener (to hot-swap the certificate), or `null` when it failed to start.
|
|
655
609
|
*/
|
|
656
|
-
async function startAgentHttpServer(getBroker, config) {
|
|
610
|
+
export async function startAgentHttpServer(getBroker, config) {
|
|
657
611
|
try {
|
|
658
612
|
const app = await createAgentHttpServer(getBroker, config);
|
|
659
613
|
await app.ready();
|
|
660
|
-
const material =
|
|
614
|
+
const material = loadAgentHttps(config.dataDir, (reason) => config.log?.error('The HTTPS bundle on disk is unusable - serving plain HTTP only', {
|
|
661
615
|
meta: { reason },
|
|
662
616
|
}));
|
|
663
|
-
const listener = await
|
|
617
|
+
const listener = await startSniffingListener({
|
|
664
618
|
httpServer: app.server,
|
|
665
619
|
port: config.port,
|
|
666
620
|
host: '0.0.0.0',
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HUB_UPDATE_TIMEOUT_MS = exports.HUB_ADDON_UPDATE_ACTION = exports.HUB_ADDON_LIST_ACTION = void 0;
|
|
4
|
-
exports.hubVersionsFromList = hubVersionsFromList;
|
|
5
|
-
exports.isBehindHub = isBehindHub;
|
|
6
|
-
exports.createAgentHubAddonControls = createAgentHubAddonControls;
|
|
7
1
|
/**
|
|
8
2
|
* Updating an addon from the agent itself (D840) — by asking the HUB.
|
|
9
3
|
*
|
|
@@ -28,18 +22,18 @@ exports.createAgentHubAddonControls = createAgentHubAddonControls;
|
|
|
28
22
|
* (`remote-addon-update-grant.ts`): own node, the hub's recorded version, audited.
|
|
29
23
|
* Any refusal reaches the UI verbatim as the update's failure, never as silence.
|
|
30
24
|
*/
|
|
31
|
-
|
|
25
|
+
import { compareSemver } from '../server-root/index.js';
|
|
32
26
|
/** Hub procedures this module calls, as `$core-caps` actions. */
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
export const HUB_ADDON_LIST_ACTION = '$core-caps.addons.list';
|
|
28
|
+
export const HUB_ADDON_UPDATE_ACTION = '$core-caps.addons.updatePackage';
|
|
35
29
|
/** An agent's deploy + reload of a large package can take minutes. */
|
|
36
|
-
|
|
30
|
+
export const HUB_UPDATE_TIMEOUT_MS = 330_000;
|
|
37
31
|
const HUB_LIST_TIMEOUT_MS = 15_000;
|
|
38
32
|
function errorMessage(err) {
|
|
39
33
|
return err instanceof Error ? err.message : String(err);
|
|
40
34
|
}
|
|
41
35
|
/** `packageName → packageVersion` from an `addons.list` answer; unreadable rows are skipped. */
|
|
42
|
-
function hubVersionsFromList(list) {
|
|
36
|
+
export function hubVersionsFromList(list) {
|
|
43
37
|
const out = new Map();
|
|
44
38
|
if (!Array.isArray(list))
|
|
45
39
|
return out;
|
|
@@ -58,8 +52,8 @@ function hubVersionsFromList(list) {
|
|
|
58
52
|
return out;
|
|
59
53
|
}
|
|
60
54
|
/** True when the hub's version is strictly newer than this node's. */
|
|
61
|
-
function isBehindHub(installed, hub) {
|
|
62
|
-
return installed !== null && hub !== null &&
|
|
55
|
+
export function isBehindHub(installed, hub) {
|
|
56
|
+
return installed !== null && hub !== null && compareSemver(hub, installed) > 0;
|
|
63
57
|
}
|
|
64
58
|
/** The hub answered `{ success: false, error }`, or something unreadable. */
|
|
65
59
|
function failureOf(result) {
|
|
@@ -70,11 +64,11 @@ function failureOf(result) {
|
|
|
70
64
|
const error = Reflect.get(result, 'error');
|
|
71
65
|
return typeof error === 'string' && error.length > 0 ? error : 'the hub reported a failure';
|
|
72
66
|
}
|
|
73
|
-
function createAgentHubAddonControls(deps) {
|
|
67
|
+
export function createAgentHubAddonControls(deps) {
|
|
74
68
|
const states = new Map();
|
|
75
69
|
const getHubVersions = async () => {
|
|
76
70
|
try {
|
|
77
|
-
const list = await deps.callHub(
|
|
71
|
+
const list = await deps.callHub(HUB_ADDON_LIST_ACTION, undefined, HUB_LIST_TIMEOUT_MS);
|
|
78
72
|
return { ok: true, versions: hubVersionsFromList(list) };
|
|
79
73
|
}
|
|
80
74
|
catch (err) {
|
|
@@ -83,7 +77,7 @@ function createAgentHubAddonControls(deps) {
|
|
|
83
77
|
};
|
|
84
78
|
const run = async (packageName, version) => {
|
|
85
79
|
try {
|
|
86
|
-
const result = await deps.callHub(
|
|
80
|
+
const result = await deps.callHub(HUB_ADDON_UPDATE_ACTION, { name: packageName, version, nodeId: deps.nodeId }, HUB_UPDATE_TIMEOUT_MS);
|
|
87
81
|
const failure = failureOf(result);
|
|
88
82
|
states.set(packageName, failure === null
|
|
89
83
|
? { status: 'done', targetVersion: version, message: null }
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createAgentHubRegistration = createAgentHubRegistration;
|
|
4
1
|
/**
|
|
5
2
|
* The agent's upward capability registration (D3).
|
|
6
3
|
*
|
|
@@ -28,8 +25,8 @@ exports.createAgentHubRegistration = createAgentHubRegistration;
|
|
|
28
25
|
* coalesced (one in flight, at most one queued, the manifest built when it
|
|
29
26
|
* goes). Nothing waits on a timer: the triggers are events, the retry is the ack.
|
|
30
27
|
*/
|
|
31
|
-
|
|
32
|
-
function createAgentHubRegistration(opts) {
|
|
28
|
+
import { callRegisterNodeWithRetry, meshErrorType, MESH_NODE_UNAUTHENTICATED_TYPE, } from '@camstack/system';
|
|
29
|
+
export function createAgentHubRegistration(opts) {
|
|
33
30
|
let sending = false;
|
|
34
31
|
let queuedReason = null;
|
|
35
32
|
const send = (reason) => {
|
|
@@ -41,12 +38,12 @@ function createAgentHubRegistration(opts) {
|
|
|
41
38
|
const broker = {
|
|
42
39
|
call: (action, params, callOpts) => opts.broker().call(action, params, callOpts),
|
|
43
40
|
};
|
|
44
|
-
|
|
41
|
+
callRegisterNodeWithRetry(broker, manifest, {
|
|
45
42
|
target: 'hub',
|
|
46
43
|
...(opts.signal !== undefined ? { signal: opts.signal } : {}),
|
|
47
44
|
log: opts.log,
|
|
48
45
|
onAttemptFailed: (err) => {
|
|
49
|
-
if (
|
|
46
|
+
if (meshErrorType(err) === MESH_NODE_UNAUTHENTICATED_TYPE)
|
|
50
47
|
opts.onCertificateRefused?.(err);
|
|
51
48
|
},
|
|
52
49
|
})
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AGENT_LOCAL_DATA_STORE_ENV = void 0;
|
|
4
|
-
exports.resolveAgentInfraCapabilities = resolveAgentInfraCapabilities;
|
|
5
1
|
/**
|
|
6
2
|
* Which infra capabilities an AGENT boots — and the one it no longer does.
|
|
7
3
|
*
|
|
@@ -47,13 +43,13 @@ exports.resolveAgentInfraCapabilities = resolveAgentInfraCapabilities;
|
|
|
47
43
|
* in this series stay in place: commit 1 is inert whenever a local engine
|
|
48
44
|
* exists, and commit 3 is documentation.
|
|
49
45
|
*/
|
|
50
|
-
|
|
46
|
+
import { INFRA_CAPABILITIES } from '@camstack/system';
|
|
51
47
|
/**
|
|
52
48
|
* The env var that puts the local engine back. Named for what it restores, not
|
|
53
49
|
* for what it disables, so an operator reading a container's environment can
|
|
54
50
|
* tell what it does without this file.
|
|
55
51
|
*/
|
|
56
|
-
|
|
52
|
+
export const AGENT_LOCAL_DATA_STORE_ENV = 'CAMSTACK_AGENT_LOCAL_DATA_STORE';
|
|
57
53
|
/** The capability an agent no longer boots. */
|
|
58
54
|
const DATA_STORE_PROVIDER = 'data-store-provider';
|
|
59
55
|
/**
|
|
@@ -91,7 +87,7 @@ function isEnabled(raw) {
|
|
|
91
87
|
*
|
|
92
88
|
* `INFRA_CAPABILITIES` itself is never touched: the hub reads the same array.
|
|
93
89
|
*/
|
|
94
|
-
function resolveAgentInfraCapabilities(env) {
|
|
95
|
-
const keepLocalStore = isEnabled(env[
|
|
96
|
-
return
|
|
90
|
+
export function resolveAgentInfraCapabilities(env) {
|
|
91
|
+
const keepLocalStore = isEnabled(env[AGENT_LOCAL_DATA_STORE_ENV]);
|
|
92
|
+
return INFRA_CAPABILITIES.filter((cap) => !HUB_ONLY_INFRA.has(cap.name) && (keepLocalStore || cap.name !== DATA_STORE_PROVIDER));
|
|
97
93
|
}
|