@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,29 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
packages: zod_1.z
|
|
20
|
-
.array(zod_1.z.object({ name: zod_1.z.string(), version: zod_1.z.string().nullable() }))
|
|
1
|
+
import { ClusterNodeHistoryBodySchema, readAllRows, } from '@camstack/system';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { errMsg } from '@camstack/types';
|
|
4
|
+
const DescriptorSchema = z.object({
|
|
5
|
+
id: z.string(),
|
|
6
|
+
name: z.string(),
|
|
7
|
+
hostname: z.string(),
|
|
8
|
+
platform: z.string(),
|
|
9
|
+
arch: z.string(),
|
|
10
|
+
cpuModel: z.string().nullable(),
|
|
11
|
+
cpuCores: z.number(),
|
|
12
|
+
memoryMB: z.number(),
|
|
13
|
+
engines: z.array(z.string()).readonly(),
|
|
14
|
+
isHub: z.boolean(),
|
|
15
|
+
localIps: z.array(z.string()).readonly(),
|
|
16
|
+
addonIds: z.array(z.string()).readonly(),
|
|
17
|
+
packages: z
|
|
18
|
+
.array(z.object({ name: z.string(), version: z.string().nullable() }))
|
|
21
19
|
.readonly()
|
|
22
20
|
.optional(),
|
|
23
21
|
});
|
|
24
22
|
/** Shape of a `settings-store.query` record's `data` for this collection. */
|
|
25
|
-
const RowDataSchema =
|
|
26
|
-
lastActive:
|
|
23
|
+
const RowDataSchema = z.object({
|
|
24
|
+
lastActive: z.number(),
|
|
27
25
|
descriptor: DescriptorSchema,
|
|
28
26
|
});
|
|
29
27
|
/**
|
|
@@ -79,13 +77,19 @@ function samePackages(a, b) {
|
|
|
79
77
|
return a === b;
|
|
80
78
|
return (a.length === b.length && a.every((p, i) => p.name === b[i]?.name && p.version === b[i]?.version));
|
|
81
79
|
}
|
|
82
|
-
class ClusterNodeHistoryStore {
|
|
80
|
+
export class ClusterNodeHistoryStore {
|
|
83
81
|
resolveSettingsStore;
|
|
84
82
|
logger;
|
|
85
83
|
declared = false;
|
|
86
|
-
|
|
84
|
+
resolveKind;
|
|
85
|
+
constructor(resolveSettingsStore, logger, resolveKind = () => null) {
|
|
87
86
|
this.resolveSettingsStore = resolveSettingsStore;
|
|
88
87
|
this.logger = logger;
|
|
88
|
+
this.resolveKind = resolveKind;
|
|
89
|
+
}
|
|
90
|
+
/** Hub wiring after `records.forOwner('cluster-agent').declare(...)`. */
|
|
91
|
+
bindRecordsKind(resolver) {
|
|
92
|
+
this.resolveKind = resolver;
|
|
89
93
|
}
|
|
90
94
|
/**
|
|
91
95
|
* Idempotently declare the typed collection. Returns the resolved provider
|
|
@@ -93,6 +97,8 @@ class ClusterNodeHistoryStore {
|
|
|
93
97
|
* unreachable) — callers treat null as "skip, best-effort".
|
|
94
98
|
*/
|
|
95
99
|
async ensureDeclared() {
|
|
100
|
+
if (this.resolveKind() !== null)
|
|
101
|
+
return this.resolveSettingsStore();
|
|
96
102
|
const store = this.resolveSettingsStore();
|
|
97
103
|
if (!store)
|
|
98
104
|
return null;
|
|
@@ -112,7 +118,7 @@ class ClusterNodeHistoryStore {
|
|
|
112
118
|
}
|
|
113
119
|
catch (err) {
|
|
114
120
|
this.logger.warn('declareCollection failed (best-effort)', {
|
|
115
|
-
meta: { error:
|
|
121
|
+
meta: { error: errMsg(err) },
|
|
116
122
|
});
|
|
117
123
|
return null;
|
|
118
124
|
}
|
|
@@ -143,21 +149,17 @@ class ClusterNodeHistoryStore {
|
|
|
143
149
|
// Carry the package roster forward. `listNodes()` does not resolve it
|
|
144
150
|
// (only the reconcile does), so writing this descriptor blind would erase
|
|
145
151
|
// the back-fill's intended set on the very next topology poll.
|
|
146
|
-
const existing = await this.
|
|
152
|
+
const existing = await this.readStored(descriptor.id);
|
|
147
153
|
const packages = descriptor.packages ?? existing?.descriptor.packages;
|
|
148
154
|
const next = packages === undefined ? descriptor : { ...descriptor, packages };
|
|
149
155
|
if (existing !== null && !this.worthWriting(existing, next))
|
|
150
156
|
return;
|
|
151
|
-
await
|
|
152
|
-
collection: COLLECTION,
|
|
153
|
-
key: descriptor.id,
|
|
154
|
-
value: { lastActive: Date.now(), descriptor: next },
|
|
155
|
-
});
|
|
157
|
+
await this.writeStored(descriptor.id, Date.now(), next);
|
|
156
158
|
}
|
|
157
159
|
catch (err) {
|
|
158
160
|
this.logger.warn('snapshot upsert failed (best-effort)', {
|
|
159
161
|
tags: { nodeId: descriptor.id },
|
|
160
|
-
meta: { error:
|
|
162
|
+
meta: { error: errMsg(err) },
|
|
161
163
|
});
|
|
162
164
|
}
|
|
163
165
|
}
|
|
@@ -172,13 +174,48 @@ class ClusterNodeHistoryStore {
|
|
|
172
174
|
return !descriptorsEqual(existing.descriptor, next);
|
|
173
175
|
}
|
|
174
176
|
/** Read one node's row, validated. `null` when absent or malformed. */
|
|
175
|
-
async
|
|
177
|
+
async readStored(nodeId) {
|
|
178
|
+
const kind = this.resolveKind();
|
|
179
|
+
if (kind !== null) {
|
|
180
|
+
const row = await kind.get(nodeId);
|
|
181
|
+
if (row === null)
|
|
182
|
+
return null;
|
|
183
|
+
const parsed = RowDataSchema.safeParse({
|
|
184
|
+
lastActive: row.at ?? 0,
|
|
185
|
+
descriptor: row.data.descriptor,
|
|
186
|
+
});
|
|
187
|
+
return parsed.success ? parsed.data : null;
|
|
188
|
+
}
|
|
189
|
+
const store = this.resolveSettingsStore();
|
|
190
|
+
if (!store)
|
|
191
|
+
return null;
|
|
192
|
+
return this.readRowLegacy(store, nodeId);
|
|
193
|
+
}
|
|
194
|
+
async readRowLegacy(store, nodeId) {
|
|
176
195
|
const raw = await store.get({ collection: COLLECTION, key: nodeId });
|
|
177
196
|
if (raw === null || raw === undefined)
|
|
178
197
|
return null;
|
|
179
198
|
const parsed = RowDataSchema.safeParse(raw);
|
|
180
199
|
return parsed.success ? parsed.data : null;
|
|
181
200
|
}
|
|
201
|
+
async writeStored(nodeId, lastActive, descriptor) {
|
|
202
|
+
const kind = this.resolveKind();
|
|
203
|
+
if (kind !== null) {
|
|
204
|
+
const body = ClusterNodeHistoryBodySchema.safeParse({ descriptor });
|
|
205
|
+
if (!body.success)
|
|
206
|
+
return;
|
|
207
|
+
await kind.put({ id: nodeId, at: lastActive, data: body.data });
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const store = this.resolveSettingsStore();
|
|
211
|
+
if (!store)
|
|
212
|
+
return;
|
|
213
|
+
await store.set({
|
|
214
|
+
collection: COLLECTION,
|
|
215
|
+
key: nodeId,
|
|
216
|
+
value: { lastActive, descriptor },
|
|
217
|
+
});
|
|
218
|
+
}
|
|
182
219
|
/** A placeholder descriptor for a node the topology pass has not seen yet. */
|
|
183
220
|
static placeholderDescriptor(nodeId) {
|
|
184
221
|
return {
|
|
@@ -206,12 +243,12 @@ class ClusterNodeHistoryStore {
|
|
|
206
243
|
if (!store)
|
|
207
244
|
return [];
|
|
208
245
|
try {
|
|
209
|
-
return (await this.
|
|
246
|
+
return (await this.readStored(nodeId))?.descriptor.packages ?? [];
|
|
210
247
|
}
|
|
211
248
|
catch (err) {
|
|
212
249
|
this.logger.warn('getPackages failed (best-effort)', {
|
|
213
250
|
tags: { nodeId },
|
|
214
|
-
meta: { error:
|
|
251
|
+
meta: { error: errMsg(err) },
|
|
215
252
|
});
|
|
216
253
|
return [];
|
|
217
254
|
}
|
|
@@ -231,7 +268,7 @@ class ClusterNodeHistoryStore {
|
|
|
231
268
|
if (!store)
|
|
232
269
|
return;
|
|
233
270
|
try {
|
|
234
|
-
const existing = await this.
|
|
271
|
+
const existing = await this.readStored(nodeId);
|
|
235
272
|
const merged = new Map((existing?.descriptor.packages ?? []).map((p) => [p.name, p]));
|
|
236
273
|
// The node's current declaration wins on version — it is the authority on
|
|
237
274
|
// what it is actually running.
|
|
@@ -241,16 +278,12 @@ class ClusterNodeHistoryStore {
|
|
|
241
278
|
...(existing?.descriptor ?? ClusterNodeHistoryStore.placeholderDescriptor(nodeId)),
|
|
242
279
|
packages: [...merged.values()],
|
|
243
280
|
};
|
|
244
|
-
await
|
|
245
|
-
collection: COLLECTION,
|
|
246
|
-
key: nodeId,
|
|
247
|
-
value: { lastActive: existing?.lastActive ?? Date.now(), descriptor },
|
|
248
|
-
});
|
|
281
|
+
await this.writeStored(nodeId, existing?.lastActive ?? Date.now(), descriptor);
|
|
249
282
|
}
|
|
250
283
|
catch (err) {
|
|
251
284
|
this.logger.warn('recordPackages failed (best-effort)', {
|
|
252
285
|
tags: { nodeId },
|
|
253
|
-
meta: { error:
|
|
286
|
+
meta: { error: errMsg(err) },
|
|
254
287
|
});
|
|
255
288
|
}
|
|
256
289
|
}
|
|
@@ -266,7 +299,7 @@ class ClusterNodeHistoryStore {
|
|
|
266
299
|
if (!store)
|
|
267
300
|
return;
|
|
268
301
|
try {
|
|
269
|
-
const existing = await this.
|
|
302
|
+
const existing = await this.readStored(nodeId);
|
|
270
303
|
if (existing === null)
|
|
271
304
|
return;
|
|
272
305
|
const current = existing.descriptor.packages;
|
|
@@ -276,13 +309,9 @@ class ClusterNodeHistoryStore {
|
|
|
276
309
|
const remaining = current.filter((p) => !drop.has(p.name));
|
|
277
310
|
if (remaining.length === current.length)
|
|
278
311
|
return;
|
|
279
|
-
await
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
value: {
|
|
283
|
-
lastActive: existing.lastActive,
|
|
284
|
-
descriptor: { ...existing.descriptor, packages: remaining },
|
|
285
|
-
},
|
|
312
|
+
await this.writeStored(nodeId, existing.lastActive, {
|
|
313
|
+
...existing.descriptor,
|
|
314
|
+
packages: remaining,
|
|
286
315
|
});
|
|
287
316
|
this.logger.info('pruned node package record', {
|
|
288
317
|
tags: { nodeId },
|
|
@@ -292,7 +321,7 @@ class ClusterNodeHistoryStore {
|
|
|
292
321
|
catch (err) {
|
|
293
322
|
this.logger.warn('prunePackages failed (best-effort)', {
|
|
294
323
|
tags: { nodeId },
|
|
295
|
-
meta: { error:
|
|
324
|
+
meta: { error: errMsg(err) },
|
|
296
325
|
});
|
|
297
326
|
}
|
|
298
327
|
}
|
|
@@ -306,6 +335,18 @@ class ClusterNodeHistoryStore {
|
|
|
306
335
|
if (!store)
|
|
307
336
|
return;
|
|
308
337
|
try {
|
|
338
|
+
const existing = await this.readStored(nodeId);
|
|
339
|
+
if (existing === null)
|
|
340
|
+
return;
|
|
341
|
+
const kind = this.resolveKind();
|
|
342
|
+
if (kind !== null) {
|
|
343
|
+
await kind.put({
|
|
344
|
+
id: nodeId,
|
|
345
|
+
at: Date.now(),
|
|
346
|
+
data: { descriptor: existing.descriptor },
|
|
347
|
+
});
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
309
350
|
await store.update({
|
|
310
351
|
collection: COLLECTION,
|
|
311
352
|
id: nodeId,
|
|
@@ -315,7 +356,7 @@ class ClusterNodeHistoryStore {
|
|
|
315
356
|
catch (err) {
|
|
316
357
|
this.logger.warn('touch failed (best-effort)', {
|
|
317
358
|
tags: { nodeId },
|
|
318
|
-
meta: { error:
|
|
359
|
+
meta: { error: errMsg(err) },
|
|
319
360
|
});
|
|
320
361
|
}
|
|
321
362
|
}
|
|
@@ -335,8 +376,17 @@ class ClusterNodeHistoryStore {
|
|
|
335
376
|
if (!store)
|
|
336
377
|
return null;
|
|
337
378
|
try {
|
|
338
|
-
const
|
|
379
|
+
const kind = this.resolveKind();
|
|
339
380
|
const result = [];
|
|
381
|
+
const rows = kind !== null
|
|
382
|
+
? (await readAllRows(kind)).map((row) => ({
|
|
383
|
+
id: row.id,
|
|
384
|
+
data: { lastActive: row.at ?? 0, descriptor: row.data.descriptor },
|
|
385
|
+
}))
|
|
386
|
+
: (await store.query({ collection: COLLECTION })).map((row) => ({
|
|
387
|
+
id: row.id,
|
|
388
|
+
data: row.data,
|
|
389
|
+
}));
|
|
340
390
|
for (const row of rows) {
|
|
341
391
|
const parsed = RowDataSchema.safeParse(row.data);
|
|
342
392
|
if (!parsed.success) {
|
|
@@ -350,7 +400,7 @@ class ClusterNodeHistoryStore {
|
|
|
350
400
|
return result;
|
|
351
401
|
}
|
|
352
402
|
catch (err) {
|
|
353
|
-
this.logger.warn('history read failed (best-effort)', { meta: { error:
|
|
403
|
+
this.logger.warn('history read failed (best-effort)', { meta: { error: errMsg(err) } });
|
|
354
404
|
return null;
|
|
355
405
|
}
|
|
356
406
|
}
|
|
@@ -370,8 +420,14 @@ class ClusterNodeHistoryStore {
|
|
|
370
420
|
// Read first: `delete` on this provider is idempotent and cannot tell a
|
|
371
421
|
// removal from a no-op, and "there was nothing to remove" is the answer
|
|
372
422
|
// that stops an operator repeating a working action forever.
|
|
373
|
-
const existed = (await this.
|
|
374
|
-
|
|
423
|
+
const existed = (await this.readStored(nodeId)) !== null;
|
|
424
|
+
const kind = this.resolveKind();
|
|
425
|
+
if (kind !== null) {
|
|
426
|
+
await kind.delete(nodeId);
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
await store.delete({ collection: COLLECTION, key: nodeId });
|
|
430
|
+
}
|
|
375
431
|
if (!existed) {
|
|
376
432
|
this.logger.info('cluster node had no history row to forget', { tags: { nodeId } });
|
|
377
433
|
return 'not-found';
|
|
@@ -382,10 +438,9 @@ class ClusterNodeHistoryStore {
|
|
|
382
438
|
catch (err) {
|
|
383
439
|
this.logger.warn('forget failed (best-effort)', {
|
|
384
440
|
tags: { nodeId },
|
|
385
|
-
meta: { error:
|
|
441
|
+
meta: { error: errMsg(err) },
|
|
386
442
|
});
|
|
387
443
|
return 'failed';
|
|
388
444
|
}
|
|
389
445
|
}
|
|
390
446
|
}
|
|
391
|
-
exports.ClusterNodeHistoryStore = ClusterNodeHistoryStore;
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HubAgentPairing = void 0;
|
|
4
1
|
/** A browse takes seconds; a list asked within this window reuses the last scan. */
|
|
5
2
|
const SCAN_REUSE_MS = 4_000;
|
|
6
3
|
/** A typed address is remembered this long without being heard again. */
|
|
7
4
|
const ADDRESS_MEMORY_MS = 30 * 60_000;
|
|
8
5
|
const DEFAULT_PORT = 4444;
|
|
9
|
-
class HubAgentPairing {
|
|
6
|
+
export class HubAgentPairing {
|
|
10
7
|
deps;
|
|
11
8
|
scanned = null;
|
|
12
9
|
scanning = null;
|
|
@@ -120,4 +117,3 @@ class HubAgentPairing {
|
|
|
120
117
|
};
|
|
121
118
|
}
|
|
122
119
|
}
|
|
123
|
-
exports.HubAgentPairing = HubAgentPairing;
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createNodeCertificatesClient = createNodeCertificatesClient;
|
|
4
1
|
/**
|
|
5
2
|
* The hub's typed handle on a node's `node-certificates` capability (D856).
|
|
6
3
|
*
|
|
@@ -9,8 +6,8 @@ exports.createNodeCertificatesClient = createNodeCertificatesClient;
|
|
|
9
6
|
* offline, capability not declared by an old agent) or a malformed answer becomes
|
|
10
7
|
* a plain error naming the node - never a cast.
|
|
11
8
|
*/
|
|
12
|
-
|
|
13
|
-
function createNodeCertificatesClient(moleculer, nodeId) {
|
|
9
|
+
import { nodeCertificatesCapability } from '@camstack/types';
|
|
10
|
+
export function createNodeCertificatesClient(moleculer, nodeId) {
|
|
14
11
|
const proxy = moleculer?.createCapabilityProxy('node-certificates', nodeId) ?? null;
|
|
15
12
|
if (proxy === null)
|
|
16
13
|
return null;
|
|
@@ -25,7 +22,7 @@ function createNodeCertificatesClient(moleculer, nodeId) {
|
|
|
25
22
|
}
|
|
26
23
|
return parsed.data;
|
|
27
24
|
};
|
|
28
|
-
const m =
|
|
25
|
+
const m = nodeCertificatesCapability.methods;
|
|
29
26
|
return {
|
|
30
27
|
getCertificates: () => call('getCertificates', m.getCertificates.output, {}),
|
|
31
28
|
prepareMeshRenewal: () => call('prepareMeshRenewal', m.prepareMeshRenewal.output, {}),
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toNodeDirectory = toNodeDirectory;
|
|
4
1
|
/**
|
|
5
2
|
* The cheap node list (D637): live liveness plus the durable history, merged.
|
|
6
3
|
*
|
|
@@ -11,7 +8,7 @@ exports.toNodeDirectory = toNodeDirectory;
|
|
|
11
8
|
* - `history === null` = unreadable, so the answer is INCOMPLETE and a picker
|
|
12
9
|
* must not call a missing id removed (D315).
|
|
13
10
|
*/
|
|
14
|
-
function toNodeDirectory(live, history) {
|
|
11
|
+
export function toNodeDirectory(live, history) {
|
|
15
12
|
const rows = history ?? [];
|
|
16
13
|
const names = new Map(rows.map((row) => [row.id, row.name]));
|
|
17
14
|
const hubs = new Set(rows.filter((row) => row.isHub).map((row) => row.id));
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* well-known-app-links — the two files a Universal/App Link needs the DOMAIN
|
|
4
3
|
* to vouch for.
|
|
@@ -19,16 +18,13 @@
|
|
|
19
18
|
* ACCEPTED and produces a valid file that fails auto-verification (the OS
|
|
20
19
|
* shows a disambiguation dialog): same trade-off the Zentik file ships with.
|
|
21
20
|
*/
|
|
22
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
-
exports.buildAppLinkFiles = buildAppLinkFiles;
|
|
24
|
-
exports.parseCertFingerprints = parseCertFingerprints;
|
|
25
21
|
/** Apple team + bundle ids, mirrored from the viewer's app.config.ts. */
|
|
26
22
|
const APPLE_TEAM_ID = 'C3F24V5NS5';
|
|
27
23
|
const BUNDLE_ID = 'com.apocaliss92.camstack';
|
|
28
24
|
const BUNDLE_ID_DEV = 'com.apocaliss92.camstack.dev';
|
|
29
25
|
/** The only path family the app claims on this domain. */
|
|
30
26
|
const VIEWER_COMPONENT = '/viewer/camstack/*';
|
|
31
|
-
function buildAppLinkFiles(androidCertSha256) {
|
|
27
|
+
export function buildAppLinkFiles(androidCertSha256) {
|
|
32
28
|
const appIds = [`${APPLE_TEAM_ID}.${BUNDLE_ID}`, `${APPLE_TEAM_ID}.${BUNDLE_ID_DEV}`];
|
|
33
29
|
return {
|
|
34
30
|
appleAppSiteAssociation: {
|
|
@@ -56,7 +52,7 @@ function buildAppLinkFiles(androidCertSha256) {
|
|
|
56
52
|
};
|
|
57
53
|
}
|
|
58
54
|
/** Parse the comma-separated env form; junk and empties dropped. */
|
|
59
|
-
function parseCertFingerprints(raw) {
|
|
55
|
+
export function parseCertFingerprints(raw) {
|
|
60
56
|
if (raw === undefined)
|
|
61
57
|
return [];
|
|
62
58
|
return raw
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AuthService = exports.SessionRevokedError = void 0;
|
|
4
|
-
const system_1 = require("@camstack/system");
|
|
1
|
+
import { AuthManager } from '@camstack/system';
|
|
5
2
|
/**
|
|
6
3
|
* A session-grade token whose server-side session is gone (logged out,
|
|
7
4
|
* revoked, expired) or cannot be checked right now. Every caller of
|
|
8
5
|
* `verifyToken` already treats a throw as "not authenticated".
|
|
9
6
|
*/
|
|
10
|
-
class SessionRevokedError extends Error {
|
|
7
|
+
export class SessionRevokedError extends Error {
|
|
11
8
|
reason;
|
|
12
9
|
constructor(reason) {
|
|
13
10
|
super(`Session is no longer valid (${reason})`);
|
|
@@ -15,14 +12,13 @@ class SessionRevokedError extends Error {
|
|
|
15
12
|
this.name = 'SessionRevokedError';
|
|
16
13
|
}
|
|
17
14
|
}
|
|
18
|
-
exports.SessionRevokedError = SessionRevokedError;
|
|
19
15
|
/** A v2 session token: string `userId`, boolean `isAdmin`, no `kind` (not a bridge/challenge). */
|
|
20
16
|
function isSessionGrade(payload) {
|
|
21
17
|
return (Reflect.get(payload, 'kind') === undefined &&
|
|
22
18
|
typeof payload.userId === 'string' &&
|
|
23
19
|
typeof payload.isAdmin === 'boolean');
|
|
24
20
|
}
|
|
25
|
-
class AuthService extends
|
|
21
|
+
export class AuthService extends AuthManager {
|
|
26
22
|
sessionCheck = null;
|
|
27
23
|
/**
|
|
28
24
|
* Wire the login-session check (`hub-sessions.ts`). From then on EVERY
|
|
@@ -53,4 +49,3 @@ class AuthService extends system_1.AuthManager {
|
|
|
53
49
|
return payload;
|
|
54
50
|
}
|
|
55
51
|
}
|
|
56
|
-
exports.AuthService = AuthService;
|
|
@@ -1,41 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
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.HandoffCodeService = exports.HANDOFF_CODE_MAX_PENDING = exports.HANDOFF_CODE_TTL_MS = exports.HANDOFF_CODE_PREFIX = void 0;
|
|
37
|
-
exports.pkceChallenge = pkceChallenge;
|
|
38
|
-
const types_1 = require("@camstack/types");
|
|
1
|
+
import { userError } from '@camstack/types';
|
|
39
2
|
/**
|
|
40
3
|
* HandoffCodeService — one-time login handoff codes for the native-app
|
|
41
4
|
* auth handoff (viewer "Sign in with CamStack").
|
|
@@ -59,26 +22,26 @@ const types_1 = require("@camstack/types");
|
|
|
59
22
|
* (the browser just re-runs the handoff), and a bounded store +
|
|
60
23
|
* prune-on-mint keeps the map from growing.
|
|
61
24
|
*/
|
|
62
|
-
|
|
25
|
+
import * as crypto from 'node:crypto';
|
|
63
26
|
/** Wire prefix — `chc_` = CamStack Handoff Code (cf. `cst_`/`csv_`). */
|
|
64
|
-
|
|
65
|
-
|
|
27
|
+
export const HANDOFF_CODE_PREFIX = 'chc_';
|
|
28
|
+
export const HANDOFF_CODE_TTL_MS = 60_000;
|
|
66
29
|
/** Hard bound on concurrently-pending codes (mint is auth-gated, so this
|
|
67
30
|
* only guards against a runaway authenticated client). */
|
|
68
|
-
|
|
31
|
+
export const HANDOFF_CODE_MAX_PENDING = 1_000;
|
|
69
32
|
/** RFC 7636 S256: `base64url(sha256(verifier))`. */
|
|
70
|
-
function pkceChallenge(verifier) {
|
|
33
|
+
export function pkceChallenge(verifier) {
|
|
71
34
|
return crypto.createHash('sha256').update(verifier).digest('base64url');
|
|
72
35
|
}
|
|
73
36
|
function hashCode(code) {
|
|
74
37
|
return crypto.createHash('sha256').update(code).digest('hex');
|
|
75
38
|
}
|
|
76
|
-
class HandoffCodeService {
|
|
39
|
+
export class HandoffCodeService {
|
|
77
40
|
now;
|
|
78
41
|
ttlMs;
|
|
79
42
|
/** Keyed by SHA-256(raw code). */
|
|
80
43
|
pending = new Map();
|
|
81
|
-
constructor(now = Date.now, ttlMs =
|
|
44
|
+
constructor(now = Date.now, ttlMs = HANDOFF_CODE_TTL_MS) {
|
|
82
45
|
this.now = now;
|
|
83
46
|
this.ttlMs = ttlMs;
|
|
84
47
|
}
|
|
@@ -87,10 +50,10 @@ class HandoffCodeService {
|
|
|
87
50
|
* legitimate flow never has anywhere near this many in flight). */
|
|
88
51
|
create(userId, options = {}) {
|
|
89
52
|
this.prune();
|
|
90
|
-
if (this.pending.size >=
|
|
91
|
-
throw
|
|
53
|
+
if (this.pending.size >= HANDOFF_CODE_MAX_PENDING) {
|
|
54
|
+
throw userError('server.handoff-busy', undefined, 'Too many pending handoff codes — try again shortly');
|
|
92
55
|
}
|
|
93
|
-
const code = `${
|
|
56
|
+
const code = `${HANDOFF_CODE_PREFIX}${crypto.randomBytes(32).toString('hex')}`;
|
|
94
57
|
const expiresAt = this.now() + this.ttlMs;
|
|
95
58
|
this.pending.set(hashCode(code), {
|
|
96
59
|
userId,
|
|
@@ -144,4 +107,3 @@ class HandoffCodeService {
|
|
|
144
107
|
}
|
|
145
108
|
}
|
|
146
109
|
}
|
|
147
|
-
exports.HandoffCodeService = HandoffCodeService;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Record kinds owned by `core-auth` (hub-resident, phase 6 / §2.8).
|
|
3
|
+
*/
|
|
4
|
+
import { defineRecordKinds, SHARE_VIEW_KINDS } from '@camstack/types';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
const ShareTokenScopeSchema = z.object({
|
|
7
|
+
kind: z.enum(SHARE_VIEW_KINDS),
|
|
8
|
+
deviceIds: z.array(z.number().int().nonnegative()).min(1).max(64),
|
|
9
|
+
talk: z.boolean().optional(),
|
|
10
|
+
});
|
|
11
|
+
export const SHARE_VIEW_TOKENS_LEGACY = 'share_view_tokens';
|
|
12
|
+
const ShareViewTokenBodySchema = z.object({
|
|
13
|
+
userId: z.string(),
|
|
14
|
+
tokenHash: z.string(),
|
|
15
|
+
tokenPrefix: z.string(),
|
|
16
|
+
scope: ShareTokenScopeSchema,
|
|
17
|
+
createdAt: z.number(),
|
|
18
|
+
expiresAt: z.number().nullable(),
|
|
19
|
+
lastUsedAt: z.number().nullish(),
|
|
20
|
+
});
|
|
21
|
+
export const coreAuthRecordKinds = defineRecordKinds({
|
|
22
|
+
'share-view-tokens': {
|
|
23
|
+
schema: ShareViewTokenBodySchema,
|
|
24
|
+
class: 'config',
|
|
25
|
+
writeTrigger: 'operator mints a share link; validate touches lastUsedAt',
|
|
26
|
+
device: 'none',
|
|
27
|
+
at: 'none',
|
|
28
|
+
indexes: [
|
|
29
|
+
{ name: 'hash', fields: ['tokenHash'], unique: true },
|
|
30
|
+
{ name: 'user', fields: ['userId'] },
|
|
31
|
+
],
|
|
32
|
+
retention: { none: 'revoke only; expiry enforced at validate' },
|
|
33
|
+
adoptsFrom: SHARE_VIEW_TOKENS_LEGACY,
|
|
34
|
+
},
|
|
35
|
+
});
|