@serfab/cadre-host 1.13.0 → 1.14.0
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/README.md +332 -423
- package/dist/bin/host.d.ts +6 -3
- package/dist/bin/host.js +301 -505
- package/dist/bin/host.js.map +1 -1
- package/dist/bin/join-output.d.ts +56 -0
- package/dist/bin/join-output.js +92 -0
- package/dist/bin/join-output.js.map +1 -0
- package/dist/bin/nat-output.d.ts +49 -0
- package/dist/bin/nat-output.js +127 -0
- package/dist/bin/nat-output.js.map +1 -0
- package/dist/hosted/claim-details.d.ts +44 -0
- package/dist/hosted/claim-details.js +66 -0
- package/dist/hosted/claim-details.js.map +1 -0
- package/dist/hosted/hosted-node-service.d.ts +318 -0
- package/dist/hosted/hosted-node-service.js +666 -0
- package/dist/hosted/hosted-node-service.js.map +1 -0
- package/dist/hosted/hosted-node-store.d.ts +40 -0
- package/dist/hosted/hosted-node-store.js +126 -0
- package/dist/hosted/hosted-node-store.js.map +1 -0
- package/dist/{donation/donation-supervisor.d.ts → hosted/hosted-node-supervisor.d.ts} +70 -53
- package/dist/hosted/hosted-node-supervisor.js +396 -0
- package/dist/hosted/hosted-node-supervisor.js.map +1 -0
- package/dist/hosted/hosted-node-watcher.d.ts +83 -0
- package/dist/hosted/hosted-node-watcher.js +224 -0
- package/dist/hosted/hosted-node-watcher.js.map +1 -0
- package/dist/hosted/index.d.ts +16 -0
- package/dist/hosted/index.js +13 -0
- package/dist/hosted/index.js.map +1 -0
- package/dist/hosted/node-status.d.ts +35 -0
- package/dist/hosted/node-status.js +61 -0
- package/dist/hosted/node-status.js.map +1 -0
- package/dist/hosted/types.d.ts +134 -0
- package/dist/hosted/types.js +22 -0
- package/dist/hosted/types.js.map +1 -0
- package/dist/index.d.ts +12 -24
- package/dist/index.js +9 -19
- package/dist/index.js.map +1 -1
- package/dist/installer/config.d.ts +4 -34
- package/dist/installer/config.js +3 -18
- package/dist/installer/config.js.map +1 -1
- package/dist/installer/index.d.ts +7 -19
- package/dist/installer/index.js +14 -69
- package/dist/installer/index.js.map +1 -1
- package/dist/installer/paths.d.ts +0 -2
- package/dist/installer/paths.js +0 -4
- package/dist/installer/paths.js.map +1 -1
- package/dist/installer/wizard.d.ts +0 -7
- package/dist/installer/wizard.js +1 -8
- package/dist/installer/wizard.js.map +1 -1
- package/dist/nat/address-resolver.d.ts +18 -17
- package/dist/nat/address-resolver.js +79 -25
- package/dist/nat/address-resolver.js.map +1 -1
- package/dist/nat/address-watch.d.ts +37 -0
- package/dist/nat/address-watch.js +84 -0
- package/dist/nat/address-watch.js.map +1 -0
- package/dist/nat/ddns/duckdns.d.ts +2 -1
- package/dist/nat/ddns/duckdns.js +5 -1
- package/dist/nat/ddns/duckdns.js.map +1 -1
- package/dist/nat/ddns/index.d.ts +6 -0
- package/dist/nat/ddns/index.js.map +1 -1
- package/dist/nat/index.d.ts +12 -9
- package/dist/nat/index.js +6 -5
- package/dist/nat/index.js.map +1 -1
- package/dist/nat/nat-service.d.ts +203 -77
- package/dist/nat/nat-service.js +602 -237
- package/dist/nat/nat-service.js.map +1 -1
- package/dist/nat/nat-store.d.ts +12 -5
- package/dist/nat/nat-store.js +114 -30
- package/dist/nat/nat-store.js.map +1 -1
- package/dist/nat/port-mapper.d.ts +83 -79
- package/dist/nat/port-mapper.js +148 -189
- package/dist/nat/port-mapper.js.map +1 -1
- package/dist/nat/reachability.d.ts +31 -15
- package/dist/nat/reachability.js +97 -22
- package/dist/nat/reachability.js.map +1 -1
- package/dist/nat/types.d.ts +78 -27
- package/dist/nat/types.js +8 -5
- package/dist/nat/types.js.map +1 -1
- package/dist/orchestrator/host-process-orchestrator.d.ts +29 -71
- package/dist/orchestrator/host-process-orchestrator.js +78 -218
- package/dist/orchestrator/host-process-orchestrator.js.map +1 -1
- package/dist/{installer/identity.d.ts → orchestrator/identity-file.d.ts} +5 -5
- package/dist/{installer/identity.js → orchestrator/identity-file.js} +6 -6
- package/dist/orchestrator/identity-file.js.map +1 -0
- package/dist/orchestrator/index.d.ts +2 -3
- package/dist/orchestrator/index.js +1 -1
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/node-identity.d.ts +6 -6
- package/dist/orchestrator/node-identity.js +7 -7
- package/dist/orchestrator/node-identity.js.map +1 -1
- package/dist/orchestrator/port-allocator.d.ts +5 -6
- package/dist/orchestrator/port-allocator.js +9 -11
- package/dist/orchestrator/port-allocator.js.map +1 -1
- package/dist/orchestrator/state-store.d.ts +3 -5
- package/dist/orchestrator/state-store.js +2 -10
- package/dist/orchestrator/state-store.js.map +1 -1
- package/dist/orchestrator/types.d.ts +33 -22
- package/dist/orchestrator/types.js.map +1 -1
- package/dist/server/error-handler.d.ts +5 -25
- package/dist/server/error-handler.js +11 -74
- package/dist/server/error-handler.js.map +1 -1
- package/dist/server/events/types.d.ts +7 -11
- package/dist/server/events/types.js +2 -1
- package/dist/server/events/types.js.map +1 -1
- package/dist/server/index.d.ts +11 -37
- package/dist/server/index.js +30 -48
- package/dist/server/index.js.map +1 -1
- package/dist/server/routes/hosted-nodes.d.ts +28 -0
- package/dist/server/routes/hosted-nodes.js +68 -0
- package/dist/server/routes/hosted-nodes.js.map +1 -0
- package/dist/server/routes/nat.d.ts +4 -1
- package/dist/server/routes/nat.js +13 -2
- package/dist/server/routes/nat.js.map +1 -1
- package/dist/server/routes/nodes.d.ts +6 -20
- package/dist/server/routes/nodes.js +7 -86
- package/dist/server/routes/nodes.js.map +1 -1
- package/dist/server/routes/settings.d.ts +2 -7
- package/dist/server/routes/settings.js +3 -7
- package/dist/server/routes/settings.js.map +1 -1
- package/dist/server/routes/status.d.ts +2 -25
- package/dist/server/routes/status.js +1 -11
- package/dist/server/routes/status.js.map +1 -1
- package/dist/server/server.js +8 -8
- package/dist/server/settings-store.d.ts +2 -2
- package/dist/server/settings-store.js +2 -2
- package/dist/server/static.js +1 -2
- package/dist/server/static.js.map +1 -1
- package/dist/ui/assets/index-4FWuU8ye.js +19 -0
- package/dist/ui/assets/index-4FWuU8ye.js.map +1 -0
- package/dist/ui/assets/index-Di9Q4d01.css +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/update/index.d.ts +2 -2
- package/dist/update/index.js +2 -2
- package/dist/update/store.d.ts +1 -1
- package/dist/update/store.js +1 -1
- package/dist/update/types.d.ts +1 -1
- package/dist/update/types.js +1 -1
- package/package.json +5 -6
- package/src/__tests__/cli-nat.smoke.test.ts +219 -212
- package/src/__tests__/cli.smoke.test.ts +1 -1
- package/src/__tests__/global-setup.ts +2 -3
- package/src/__tests__/orchestrator-claim-secret.test.ts +112 -0
- package/src/__tests__/orchestrator-handle-contract.ts +134 -134
- package/src/__tests__/orchestrator-node-identity.test.ts +182 -203
- package/src/__tests__/orchestrator-ports.test.ts +124 -143
- package/src/__tests__/orchestrator-push.test.ts +138 -146
- package/src/__tests__/orchestrator.test.ts +1019 -1063
- package/src/bin/host.ts +368 -553
- package/src/bin/join-output.ts +120 -0
- package/src/bin/nat-output.ts +177 -0
- package/src/{donation → hosted}/__tests__/fake-orchestrator.test.ts +173 -173
- package/src/{donation → hosted}/__tests__/fake-orchestrator.ts +265 -243
- package/src/hosted/__tests__/hosted-node-service.test.ts +609 -0
- package/src/hosted/__tests__/hosted-node-supervisor.test.ts +474 -0
- package/src/hosted/__tests__/test-invitation.ts +33 -0
- package/src/hosted/claim-details.ts +96 -0
- package/src/hosted/hosted-node-service.ts +795 -0
- package/src/hosted/hosted-node-store.ts +144 -0
- package/src/hosted/hosted-node-supervisor.ts +439 -0
- package/src/hosted/hosted-node-watcher.ts +250 -0
- package/src/hosted/index.ts +53 -0
- package/src/hosted/node-status.ts +71 -0
- package/src/hosted/types.ts +153 -0
- package/src/index.ts +72 -91
- package/src/installer/__tests__/config.test.ts +15 -40
- package/src/installer/__tests__/installer.smoke.test.ts +5 -86
- package/src/installer/__tests__/wizard.test.ts +56 -64
- package/src/installer/config.ts +135 -175
- package/src/installer/index.ts +15 -83
- package/src/installer/paths.ts +104 -109
- package/src/installer/wizard.ts +87 -103
- package/src/nat/__tests__/address-resolver.test.ts +107 -82
- package/src/nat/__tests__/nat-service.test.ts +433 -408
- package/src/nat/__tests__/nat-store.test.ts +127 -93
- package/src/nat/__tests__/port-mapper.test.ts +30 -191
- package/src/nat/__tests__/reachability.test.ts +96 -13
- package/src/nat/address-resolver.ts +94 -52
- package/src/nat/address-watch.ts +92 -0
- package/src/nat/ddns/duckdns.ts +5 -1
- package/src/nat/ddns/index.ts +6 -0
- package/src/nat/index.ts +87 -71
- package/src/nat/nat-service.ts +889 -501
- package/src/nat/nat-store.ts +237 -154
- package/src/nat/port-mapper.ts +241 -329
- package/src/nat/reachability.ts +134 -25
- package/src/nat/types.ts +209 -159
- package/src/{installer/__tests__/identity.test.ts → orchestrator/__tests__/identity-file.test.ts} +34 -34
- package/src/orchestrator/__tests__/node-identity.test.ts +62 -62
- package/src/orchestrator/host-process-orchestrator.ts +81 -243
- package/src/{installer/identity.ts → orchestrator/identity-file.ts} +56 -56
- package/src/orchestrator/index.ts +9 -10
- package/src/orchestrator/node-identity.ts +41 -41
- package/src/orchestrator/port-allocator.ts +71 -73
- package/src/orchestrator/state-store.ts +76 -90
- package/src/orchestrator/types.ts +34 -23
- package/src/server/__tests__/bus.test.ts +38 -38
- package/src/server/__tests__/error-handler.test.ts +81 -118
- package/src/server/__tests__/fakes.ts +40 -44
- package/src/server/__tests__/hosted-nodes-route.test.ts +160 -0
- package/src/server/__tests__/nodes-route.test.ts +114 -283
- package/src/server/__tests__/publishers.test.ts +193 -180
- package/src/server/__tests__/server.smoke.test.ts +134 -223
- package/src/server/__tests__/settings-route.test.ts +179 -195
- package/src/server/__tests__/sse-route.test.ts +119 -119
- package/src/server/__tests__/status-route.test.ts +82 -121
- package/src/server/error-handler.ts +112 -178
- package/src/server/events/types.ts +20 -24
- package/src/server/index.ts +223 -267
- package/src/server/routes/hosted-nodes.ts +83 -0
- package/src/server/routes/nat.ts +75 -63
- package/src/server/routes/nodes.ts +96 -188
- package/src/server/routes/settings.ts +115 -123
- package/src/server/routes/status.ts +102 -134
- package/src/server/server.ts +8 -8
- package/src/server/settings-store.ts +2 -2
- package/src/server/static.ts +1 -2
- package/src/update/index.ts +2 -2
- package/src/update/store.ts +1 -1
- package/src/update/types.ts +1 -1
- package/dist/auth/duration.d.ts +0 -1
- package/dist/auth/duration.js +0 -32
- package/dist/auth/duration.js.map +0 -1
- package/dist/auth/index.d.ts +0 -11
- package/dist/auth/index.js +0 -9
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/self-label.d.ts +0 -36
- package/dist/auth/self-label.js +0 -57
- package/dist/auth/self-label.js.map +0 -1
- package/dist/auth/trust-circle-store.d.ts +0 -26
- package/dist/auth/trust-circle-store.js +0 -129
- package/dist/auth/trust-circle-store.js.map +0 -1
- package/dist/auth/trust-circle.d.ts +0 -156
- package/dist/auth/trust-circle.js +0 -307
- package/dist/auth/trust-circle.js.map +0 -1
- package/dist/auth/types.d.ts +0 -84
- package/dist/auth/types.js +0 -24
- package/dist/auth/types.js.map +0 -1
- package/dist/donation/donation-service.d.ts +0 -389
- package/dist/donation/donation-service.js +0 -828
- package/dist/donation/donation-service.js.map +0 -1
- package/dist/donation/donation-store.d.ts +0 -51
- package/dist/donation/donation-store.js +0 -152
- package/dist/donation/donation-store.js.map +0 -1
- package/dist/donation/donation-supervisor.js +0 -364
- package/dist/donation/donation-supervisor.js.map +0 -1
- package/dist/donation/grant-service.d.ts +0 -80
- package/dist/donation/grant-service.js +0 -210
- package/dist/donation/grant-service.js.map +0 -1
- package/dist/donation/grant-store.d.ts +0 -38
- package/dist/donation/grant-store.js +0 -135
- package/dist/donation/grant-store.js.map +0 -1
- package/dist/donation/index.d.ts +0 -20
- package/dist/donation/index.js +0 -17
- package/dist/donation/index.js.map +0 -1
- package/dist/donation/types.d.ts +0 -224
- package/dist/donation/types.js +0 -40
- package/dist/donation/types.js.map +0 -1
- package/dist/installer/identity.js.map +0 -1
- package/dist/owner/index.d.ts +0 -11
- package/dist/owner/index.js +0 -11
- package/dist/owner/index.js.map +0 -1
- package/dist/owner/owner-node-client.d.ts +0 -110
- package/dist/owner/owner-node-client.js +0 -184
- package/dist/owner/owner-node-client.js.map +0 -1
- package/dist/server/routes/bootstrap-node-validation.d.ts +0 -115
- package/dist/server/routes/bootstrap-node-validation.js +0 -197
- package/dist/server/routes/bootstrap-node-validation.js.map +0 -1
- package/dist/server/routes/grants-admin.d.ts +0 -29
- package/dist/server/routes/grants-admin.js +0 -58
- package/dist/server/routes/grants-admin.js.map +0 -1
- package/dist/server/routes/grants.d.ts +0 -28
- package/dist/server/routes/grants.js +0 -165
- package/dist/server/routes/grants.js.map +0 -1
- package/dist/server/routes/provision-request-validation.d.ts +0 -45
- package/dist/server/routes/provision-request-validation.js +0 -97
- package/dist/server/routes/provision-request-validation.js.map +0 -1
- package/dist/server/routes/strands.d.ts +0 -20
- package/dist/server/routes/strands.js +0 -38
- package/dist/server/routes/strands.js.map +0 -1
- package/dist/server/routes/trust-circle.d.ts +0 -17
- package/dist/server/routes/trust-circle.js +0 -35
- package/dist/server/routes/trust-circle.js.map +0 -1
- package/dist/strands/index.d.ts +0 -7
- package/dist/strands/index.js +0 -6
- package/dist/strands/index.js.map +0 -1
- package/dist/strands/strand-service.d.ts +0 -50
- package/dist/strands/strand-service.js +0 -103
- package/dist/strands/strand-service.js.map +0 -1
- package/dist/strands/types.d.ts +0 -93
- package/dist/strands/types.js +0 -23
- package/dist/strands/types.js.map +0 -1
- package/dist/ui/assets/index-C6nELNfC.js +0 -23
- package/dist/ui/assets/index-C6nELNfC.js.map +0 -1
- package/dist/ui/assets/index-TtIYSeZO.css +0 -1
- package/src/__tests__/cli-invite.smoke.test.ts +0 -192
- package/src/__tests__/host-owner.smoke.test.ts +0 -123
- package/src/__tests__/orchestrator-owner.test.ts +0 -234
- package/src/__tests__/orchestrator-pin-keys.test.ts +0 -152
- package/src/auth/__tests__/duration.test.ts +0 -28
- package/src/auth/__tests__/self-label.test.ts +0 -85
- package/src/auth/__tests__/trust-circle-integration.test.ts +0 -152
- package/src/auth/__tests__/trust-circle-store.test.ts +0 -115
- package/src/auth/__tests__/trust-circle.test.ts +0 -445
- package/src/auth/duration.ts +0 -33
- package/src/auth/index.ts +0 -21
- package/src/auth/self-label.ts +0 -74
- package/src/auth/trust-circle-store.ts +0 -155
- package/src/auth/trust-circle.ts +0 -395
- package/src/auth/types.ts +0 -99
- package/src/donation/__tests__/donation-service.test.ts +0 -1193
- package/src/donation/__tests__/donation-supervisor.test.ts +0 -548
- package/src/donation/__tests__/grant-service.test.ts +0 -213
- package/src/donation/__tests__/grant-store.test.ts +0 -113
- package/src/donation/donation-service.ts +0 -1033
- package/src/donation/donation-store.ts +0 -176
- package/src/donation/donation-supervisor.ts +0 -411
- package/src/donation/grant-service.ts +0 -252
- package/src/donation/grant-store.ts +0 -152
- package/src/donation/index.ts +0 -65
- package/src/donation/types.ts +0 -260
- package/src/owner/__tests__/owner-node-client.test.ts +0 -194
- package/src/owner/index.ts +0 -12
- package/src/owner/owner-node-client.ts +0 -259
- package/src/server/__tests__/bootstrap-node-validation.test.ts +0 -138
- package/src/server/__tests__/grants-admin-route.test.ts +0 -171
- package/src/server/__tests__/grants-route.test.ts +0 -394
- package/src/server/__tests__/strands-route.test.ts +0 -234
- package/src/server/routes/bootstrap-node-validation.ts +0 -206
- package/src/server/routes/grants-admin.ts +0 -74
- package/src/server/routes/grants.ts +0 -193
- package/src/server/routes/provision-request-validation.ts +0 -105
- package/src/server/routes/strands.ts +0 -54
- package/src/server/routes/trust-circle.ts +0 -49
- package/src/strands/__tests__/strand-service.test.ts +0 -165
- package/src/strands/index.ts +0 -14
- package/src/strands/strand-service.ts +0 -135
- package/src/strands/types.ts +0 -102
package/src/index.ts
CHANGED
|
@@ -4,18 +4,12 @@
|
|
|
4
4
|
* This package is the sibling of @serfab/cadre-provider for self-hosted
|
|
5
5
|
* basement-PC deployments. It depends on @serfab/cadre-provider for the
|
|
6
6
|
* orchestration interface and container lifecycle types but ships its own
|
|
7
|
-
* orchestrator (host processes, not Docker),
|
|
8
|
-
*
|
|
7
|
+
* orchestrator (host processes, not Docker), the hosted-node service behind
|
|
8
|
+
* "Join a cadre", installer, NAT layer, and local management UI.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* under src
|
|
13
|
-
*
|
|
14
|
-
* - cadre-host-process-orchestrator [DONE]
|
|
15
|
-
* - cadre-host-trust-circle [DONE]
|
|
16
|
-
* - cadre-host-nat
|
|
17
|
-
* - cadre-host-installer
|
|
18
|
-
* - cadre-host-local-ui
|
|
10
|
+
* HostProcessOrchestrator, the hosted-node layer, the NAT layer, the
|
|
11
|
+
* installer, and the local UI server each live in a sibling subdirectory
|
|
12
|
+
* under src/.
|
|
19
13
|
*/
|
|
20
14
|
|
|
21
15
|
export type {
|
|
@@ -27,84 +21,57 @@ export type {
|
|
|
27
21
|
ContainerResources,
|
|
28
22
|
} from '@serfab/cadre-provider';
|
|
29
23
|
|
|
30
|
-
export { HostProcessOrchestrator
|
|
24
|
+
export { HostProcessOrchestrator } from './orchestrator/index.js';
|
|
31
25
|
export type {
|
|
32
26
|
HostProcessConfig,
|
|
27
|
+
HostedSpawnRequest,
|
|
33
28
|
PersistedHandle,
|
|
34
29
|
ManagedNodeInfo,
|
|
35
30
|
NodeStateListener,
|
|
36
31
|
NodePorts,
|
|
37
|
-
OwnerAdminEndpoint,
|
|
38
|
-
OwnerSpawnConfig,
|
|
39
32
|
} from './orchestrator/index.js';
|
|
40
33
|
|
|
41
|
-
|
|
42
|
-
export type { OwnerNodeClientOptions } from './owner/index.js';
|
|
43
|
-
|
|
44
|
-
export {
|
|
45
|
-
TrustCircleService,
|
|
46
|
-
TrustCircleStore,
|
|
47
|
-
TrustCircleError,
|
|
48
|
-
createTrustCircleHandlers,
|
|
49
|
-
parseDuration,
|
|
50
|
-
ensureSelfLabel,
|
|
51
|
-
DEFAULT_INVITE_TTL_MS,
|
|
52
|
-
} from './auth/index.js';
|
|
53
|
-
export type {
|
|
54
|
-
TrustCircleServiceOptions,
|
|
55
|
-
CadreNodeLike,
|
|
56
|
-
TrustCircleMember,
|
|
57
|
-
PendingInvite,
|
|
58
|
-
TrustCircleFile,
|
|
59
|
-
TrustCircleSnapshot,
|
|
60
|
-
TrustCircleHandlers,
|
|
61
|
-
TrustCircleErrorCode,
|
|
62
|
-
} from './auth/index.js';
|
|
63
|
-
|
|
64
|
-
/* ──────────────── strand management ──────────────── */
|
|
65
|
-
|
|
66
|
-
export { StrandService, StrandError, createStrandHandlers } from './strands/index.js';
|
|
67
|
-
export type {
|
|
68
|
-
StrandServiceOptions,
|
|
69
|
-
CadreNodeLike as StrandCadreNodeLike,
|
|
70
|
-
StrandSummary,
|
|
71
|
-
StrandListSnapshot,
|
|
72
|
-
StrandRemovalResult,
|
|
73
|
-
StrandHandlers,
|
|
74
|
-
StrandErrorCode,
|
|
75
|
-
} from './strands/index.js';
|
|
76
|
-
|
|
77
|
-
/* ──────────────── donation grant tokens ──────────────── */
|
|
34
|
+
/* ──────────────── hosted nodes ("Join a cadre") ──────────────── */
|
|
78
35
|
|
|
79
36
|
export {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
37
|
+
HostedNodeStore,
|
|
38
|
+
HostedNodeService,
|
|
39
|
+
HostedNodeSupervisor,
|
|
40
|
+
HostedNodeError,
|
|
41
|
+
HOSTED_NODE_SPAWNING_TTL_MS,
|
|
42
|
+
HOSTED_NODE_REAP_SWEEP_MS,
|
|
43
|
+
HOSTED_NODE_CLAIM_POLL_MS,
|
|
44
|
+
HOSTED_NODE_CONNECTED_POLL_MS,
|
|
45
|
+
HOSTED_NODE_RESPAWN_BACKOFF_BASE_MS,
|
|
46
|
+
HOSTED_NODE_RESPAWN_BACKOFF_MAX_MS,
|
|
47
|
+
HOSTED_NODE_RESPAWN_MAX_ATTEMPTS,
|
|
48
|
+
HOSTED_NODE_RESPAWN_HEALTHY_MS,
|
|
49
|
+
HOSTED_NODE_RESPAWN_SWEEP_MS,
|
|
50
|
+
PLACEHOLDER_PARTY,
|
|
51
|
+
} from './hosted/index.js';
|
|
89
52
|
export type {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
53
|
+
ClaimDetails,
|
|
54
|
+
HostedNode,
|
|
55
|
+
HostedNodeAddressSource,
|
|
56
|
+
HostedNodeChange,
|
|
57
|
+
HostedNodeChangeKind,
|
|
58
|
+
HostedNodeChangeListener,
|
|
59
|
+
HostedNodeErrorCode,
|
|
60
|
+
HostedNodeClaimJoin,
|
|
61
|
+
HostedNodeFile,
|
|
62
|
+
HostedNodeInvitationJoin,
|
|
63
|
+
HostedNodeJoin,
|
|
64
|
+
HostedNodeJoinView,
|
|
65
|
+
HostedNodeOrchestrator,
|
|
66
|
+
HostedNodeServiceOptions,
|
|
67
|
+
HostedNodeStatus,
|
|
68
|
+
HostedNodeSupervisorOptions,
|
|
69
|
+
HostedNodeView,
|
|
70
|
+
JoinOptions,
|
|
71
|
+
RespawnOptions,
|
|
72
|
+
RespawnResult,
|
|
73
|
+
SupervisedOrchestrator,
|
|
74
|
+
} from './hosted/index.js';
|
|
108
75
|
|
|
109
76
|
export { Installer, readHostConfig, updateHostConfig, writeHostConfig } from './installer/index.js';
|
|
110
77
|
export type {
|
|
@@ -179,11 +146,16 @@ export {
|
|
|
179
146
|
NatStore,
|
|
180
147
|
NatError,
|
|
181
148
|
createNatHandlers,
|
|
149
|
+
NAT_UNMAP_GRACE_MS,
|
|
150
|
+
NAT_RECONCILE_INTERVAL_MS,
|
|
151
|
+
NAT_IP_REDETECT_INTERVAL_MS,
|
|
182
152
|
ExternalIpDetector,
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
153
|
+
UpnpPortMapper,
|
|
154
|
+
pickLanAddress,
|
|
155
|
+
evaluateNodeReachability,
|
|
156
|
+
evaluateHostReachability,
|
|
157
|
+
buildPublicAddresses,
|
|
158
|
+
isPublicIpv4,
|
|
187
159
|
BUILTIN_PROVIDERS,
|
|
188
160
|
getProvider,
|
|
189
161
|
listProviders,
|
|
@@ -196,30 +168,41 @@ export {
|
|
|
196
168
|
KEYTAR_SERVICE,
|
|
197
169
|
DEFAULT_LEASE_TTL_MS,
|
|
198
170
|
DEFAULT_REFRESH_MS,
|
|
171
|
+
GATEWAY_DISCOVERY_TIMEOUT_MS,
|
|
199
172
|
isPlausibleIp,
|
|
200
173
|
} from './nat/index.js';
|
|
201
174
|
export type {
|
|
202
175
|
NatServiceOptions,
|
|
203
|
-
|
|
176
|
+
NatNodeSource,
|
|
177
|
+
NatChangeListener,
|
|
178
|
+
NodeAddressesStaleListener,
|
|
204
179
|
NatStatusSnapshot,
|
|
180
|
+
NatGatewayStatus,
|
|
205
181
|
NatDdnsStatus,
|
|
206
182
|
NatSettingsFile,
|
|
207
183
|
NatDdnsSettings,
|
|
208
184
|
NatHandlers,
|
|
209
185
|
NatErrorCode,
|
|
210
|
-
PortForwardMode,
|
|
211
186
|
DirectReachability,
|
|
187
|
+
NodeReachability,
|
|
188
|
+
NodeVerdict,
|
|
189
|
+
PortKind,
|
|
190
|
+
PortRoute,
|
|
191
|
+
PortRouteSource,
|
|
192
|
+
ManualForward,
|
|
193
|
+
ManualForwardPatch,
|
|
212
194
|
DdnsProviderInfo,
|
|
213
195
|
DdnsConfigField,
|
|
214
196
|
ExternalIpResult,
|
|
215
197
|
RouterIpProbe,
|
|
216
198
|
ExternalIpDetectorOptions,
|
|
217
199
|
PortMapper,
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
200
|
+
GatewayInfo,
|
|
201
|
+
PortMapRequest,
|
|
202
|
+
PortMapResult,
|
|
203
|
+
NodeVerdictInput,
|
|
204
|
+
NodeVerdictResult,
|
|
205
|
+
PublicAddressInput,
|
|
223
206
|
DdnsProvider,
|
|
224
207
|
DdnsProviderDeps,
|
|
225
208
|
DdnsUpdaterOptions,
|
|
@@ -236,8 +219,6 @@ export {
|
|
|
236
219
|
HostSettingsStore,
|
|
237
220
|
} from './server/index.js';
|
|
238
221
|
export type {
|
|
239
|
-
FounderServices,
|
|
240
|
-
HostRole,
|
|
241
222
|
LocalUiServer,
|
|
242
223
|
LocalUiServerOptions,
|
|
243
224
|
LocalUiEvent,
|
|
@@ -3,7 +3,7 @@ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
|
|
6
|
-
import { readHostConfig, updateHostConfig, writeHostConfig,
|
|
6
|
+
import { readHostConfig, updateHostConfig, writeHostConfig, type HostConfigFile } from '../config.js';
|
|
7
7
|
|
|
8
8
|
describe('host.config.json round-trip', () => {
|
|
9
9
|
let tmp: string;
|
|
@@ -18,12 +18,10 @@ describe('host.config.json round-trip', () => {
|
|
|
18
18
|
|
|
19
19
|
function makeCfg(overrides: Partial<HostConfigFile> = {}): HostConfigFile {
|
|
20
20
|
return {
|
|
21
|
-
version:
|
|
21
|
+
version: 3,
|
|
22
22
|
installId: 'abc123',
|
|
23
23
|
uiPort: 8765,
|
|
24
|
-
libp2pPort: 4001,
|
|
25
24
|
dataDir: tmp,
|
|
26
|
-
identityPath: join(tmp, 'identity.key'),
|
|
27
25
|
upnpEnabled: true,
|
|
28
26
|
installedAt: '2026-01-01T00:00:00.000Z',
|
|
29
27
|
installerVersion: '0.6.0',
|
|
@@ -39,10 +37,11 @@ describe('host.config.json round-trip', () => {
|
|
|
39
37
|
expect(readHostConfig(path)).toEqual(cfg);
|
|
40
38
|
});
|
|
41
39
|
|
|
42
|
-
|
|
40
|
+
// A version-2 file is a founder-era install: it is refused, never migrated.
|
|
41
|
+
it('rejects v2 files without rewriting them', () => {
|
|
43
42
|
const path = join(tmp, 'host.config.json');
|
|
44
|
-
const
|
|
45
|
-
version:
|
|
43
|
+
const v2 = {
|
|
44
|
+
version: 2,
|
|
46
45
|
installId: 'abc123',
|
|
47
46
|
uiPort: 8765,
|
|
48
47
|
libp2pPort: 4001,
|
|
@@ -51,10 +50,12 @@ describe('host.config.json round-trip', () => {
|
|
|
51
50
|
upnpEnabled: true,
|
|
52
51
|
installedAt: '2026-01-01T00:00:00.000Z',
|
|
53
52
|
installerVersion: '0.6.0',
|
|
53
|
+
updates: { autoApply: false },
|
|
54
|
+
ownCadre: { enabled: true },
|
|
54
55
|
};
|
|
55
|
-
const raw = JSON.stringify(
|
|
56
|
+
const raw = JSON.stringify(v2);
|
|
56
57
|
writeFileSync(path, raw);
|
|
57
|
-
expect(() => readHostConfig(path)).toThrow(/unsupported version=
|
|
58
|
+
expect(() => readHostConfig(path)).toThrow(/unsupported version=2/);
|
|
58
59
|
expect(readFileSync(path, 'utf8')).toBe(raw);
|
|
59
60
|
});
|
|
60
61
|
|
|
@@ -79,20 +80,20 @@ describe('host.config.json round-trip', () => {
|
|
|
79
80
|
|
|
80
81
|
it('rejects missing required fields', () => {
|
|
81
82
|
const path = join(tmp, 'host.config.json');
|
|
82
|
-
writeFileSync(path, JSON.stringify({ version:
|
|
83
|
+
writeFileSync(path, JSON.stringify({ version: 3, uiPort: 1234 }));
|
|
83
84
|
expect(() => readHostConfig(path)).toThrow(/missing required fields/);
|
|
84
85
|
});
|
|
85
86
|
|
|
86
87
|
it('refuses to write the wrong version', () => {
|
|
87
88
|
const path = join(tmp, 'host.config.json');
|
|
88
|
-
expect(() => writeHostConfig(path, { ...makeCfg(), version:
|
|
89
|
+
expect(() => writeHostConfig(path, { ...makeCfg(), version: 2 as never })).toThrow(/version=2/);
|
|
89
90
|
});
|
|
90
91
|
|
|
91
92
|
it('update patches fields and re-stamps the current version', () => {
|
|
92
93
|
const path = join(tmp, 'host.config.json');
|
|
93
94
|
writeHostConfig(path, makeCfg());
|
|
94
95
|
const next = updateHostConfig(path, { updates: { autoApply: true } });
|
|
95
|
-
expect(next.version).toBe(
|
|
96
|
+
expect(next.version).toBe(3);
|
|
96
97
|
expect(next.updates).toEqual({ autoApply: true });
|
|
97
98
|
expect(next.installId).toBe('abc123');
|
|
98
99
|
expect(readHostConfig(path)).toEqual(next);
|
|
@@ -102,33 +103,7 @@ describe('host.config.json round-trip', () => {
|
|
|
102
103
|
const path = join(tmp, 'host.config.json');
|
|
103
104
|
writeHostConfig(path, makeCfg());
|
|
104
105
|
const patch = { version: 1 } as unknown as Partial<Omit<HostConfigFile, 'version'>>;
|
|
105
|
-
expect(updateHostConfig(path, patch).version).toBe(
|
|
106
|
-
expect(readHostConfig(path).version).toBe(
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('round-trips ownCadre and hostOwnsCadre reflects it', () => {
|
|
110
|
-
const path = join(tmp, 'host.config.json');
|
|
111
|
-
const cfg = makeCfg({ ownCadre: { enabled: true } });
|
|
112
|
-
writeHostConfig(path, cfg);
|
|
113
|
-
const read = readHostConfig(path);
|
|
114
|
-
expect(read.ownCadre).toEqual({ enabled: true });
|
|
115
|
-
expect(hostOwnsCadre(read)).toBe(true);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('treats an absent ownCadre as donor-only (hostOwnsCadre false)', () => {
|
|
119
|
-
// A pre-ownCadre v2 config (field absent) must read back cleanly and be
|
|
120
|
-
// donor-only — no migration, field stays absent.
|
|
121
|
-
const path = join(tmp, 'host.config.json');
|
|
122
|
-
const cfg = makeCfg();
|
|
123
|
-
writeHostConfig(path, cfg);
|
|
124
|
-
const read = readHostConfig(path);
|
|
125
|
-
expect(read.ownCadre).toBeUndefined();
|
|
126
|
-
expect(hostOwnsCadre(read)).toBe(false);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('rejects a malformed ownCadre shape', () => {
|
|
130
|
-
const path = join(tmp, 'host.config.json');
|
|
131
|
-
writeFileSync(path, JSON.stringify({ ...makeCfg(), ownCadre: { enabled: 'yes' } }));
|
|
132
|
-
expect(() => readHostConfig(path)).toThrow(/missing required fields/);
|
|
106
|
+
expect(updateHostConfig(path, patch).version).toBe(3);
|
|
107
|
+
expect(readHostConfig(path).version).toBe(3);
|
|
133
108
|
});
|
|
134
109
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
2
|
import { existsSync, mkdtempSync, rmSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
@@ -45,16 +45,14 @@ describe('Installer smoke', () => {
|
|
|
45
45
|
rmSync(tmp, { recursive: true, force: true });
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
it('install creates config +
|
|
48
|
+
it('install creates config + invokes service-host', async () => {
|
|
49
49
|
const fake = new FakeServiceHost();
|
|
50
50
|
const installer = new Installer({ platform: 'linux', installerVersion: 'test-1.0.0' });
|
|
51
51
|
const result = await installer.install({
|
|
52
52
|
nonInteractive: true,
|
|
53
53
|
dataDir: tmp,
|
|
54
54
|
uiPort: 19999,
|
|
55
|
-
libp2pPort: 14001,
|
|
56
55
|
openBrowser: false,
|
|
57
|
-
noInvite: true,
|
|
58
56
|
serviceHost: fake,
|
|
59
57
|
});
|
|
60
58
|
|
|
@@ -64,7 +62,8 @@ describe('Installer smoke', () => {
|
|
|
64
62
|
expect(result.configPath).toBe(join(tmp, 'host.config.json'));
|
|
65
63
|
|
|
66
64
|
expect(existsSync(join(tmp, 'host.config.json'))).toBe(true);
|
|
67
|
-
|
|
65
|
+
// The host holds no identity of its own; each node writes its own key in its workdir.
|
|
66
|
+
expect(existsSync(join(tmp, 'identity.key'))).toBe(false);
|
|
68
67
|
expect(existsSync(join(tmp, 'nat.json'))).toBe(true);
|
|
69
68
|
expect(existsSync(join(tmp, 'logs'))).toBe(true);
|
|
70
69
|
|
|
@@ -79,7 +78,6 @@ describe('Installer smoke', () => {
|
|
|
79
78
|
nonInteractive: true,
|
|
80
79
|
dataDir: tmp,
|
|
81
80
|
uiPort: 19995,
|
|
82
|
-
libp2pPort: 14005,
|
|
83
81
|
noService: true,
|
|
84
82
|
serviceHost: fake,
|
|
85
83
|
});
|
|
@@ -88,35 +86,9 @@ describe('Installer smoke', () => {
|
|
|
88
86
|
expect(result.serviceName).toBeUndefined();
|
|
89
87
|
expect(result.uiUrl).toBe('http://127.0.0.1:19995/');
|
|
90
88
|
expect(existsSync(join(tmp, 'host.config.json'))).toBe(true);
|
|
91
|
-
expect(existsSync(join(tmp, 'identity.key'))).toBe(true);
|
|
92
89
|
expect(existsSync(join(tmp, 'nat.json'))).toBe(true);
|
|
93
90
|
});
|
|
94
91
|
|
|
95
|
-
it('noService on an interactive install skips the enrollment-invite fetch', async () => {
|
|
96
|
-
const fetchSpy = vi.spyOn(globalThis, 'fetch');
|
|
97
|
-
try {
|
|
98
|
-
const installer = new Installer({ platform: 'linux', installerVersion: 'test-1.0.0' });
|
|
99
|
-
const result = await installer.install({
|
|
100
|
-
nonInteractive: false,
|
|
101
|
-
noService: true,
|
|
102
|
-
wizard: async () => ({
|
|
103
|
-
dataDir: tmp,
|
|
104
|
-
uiPort: 19994,
|
|
105
|
-
libp2pPort: 14006,
|
|
106
|
-
upnpEnabled: false,
|
|
107
|
-
configureDdns: false,
|
|
108
|
-
ownCadre: false,
|
|
109
|
-
}),
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
expect(fetchSpy).not.toHaveBeenCalled();
|
|
113
|
-
expect(result.enrollmentInvite).toBeUndefined();
|
|
114
|
-
expect(existsSync(join(tmp, 'host.config.json'))).toBe(true);
|
|
115
|
-
} finally {
|
|
116
|
-
fetchSpy.mockRestore();
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
|
|
120
92
|
it('uninstall removes data only when --remove-data is set', async () => {
|
|
121
93
|
const fake = new FakeServiceHost();
|
|
122
94
|
const installer = new Installer({ platform: 'linux' });
|
|
@@ -124,9 +96,7 @@ describe('Installer smoke', () => {
|
|
|
124
96
|
nonInteractive: true,
|
|
125
97
|
dataDir: tmp,
|
|
126
98
|
uiPort: 19998,
|
|
127
|
-
libp2pPort: 14002,
|
|
128
99
|
openBrowser: false,
|
|
129
|
-
noInvite: true,
|
|
130
100
|
serviceHost: fake,
|
|
131
101
|
});
|
|
132
102
|
expect(existsSync(join(tmp, 'host.config.json'))).toBe(true);
|
|
@@ -148,7 +118,6 @@ describe('Installer smoke', () => {
|
|
|
148
118
|
system: true,
|
|
149
119
|
serviceHost: fake,
|
|
150
120
|
openBrowser: false,
|
|
151
|
-
noInvite: true,
|
|
152
121
|
})).rejects.toThrow(/system.*not yet supported/);
|
|
153
122
|
expect(fake.installCalls).toHaveLength(0);
|
|
154
123
|
});
|
|
@@ -162,15 +131,13 @@ describe('Installer smoke', () => {
|
|
|
162
131
|
// returned answers stand on their own.
|
|
163
132
|
const fake = new FakeServiceHost();
|
|
164
133
|
const installer = new Installer({ platform: 'linux' });
|
|
165
|
-
let seenDefaults: { dataDir: string; uiPort: number;
|
|
134
|
+
let seenDefaults: { dataDir: string; uiPort: number; upnpEnabled: boolean } | undefined;
|
|
166
135
|
await installer.install({
|
|
167
136
|
nonInteractive: false,
|
|
168
137
|
dataDir: tmp,
|
|
169
138
|
uiPort: 19997,
|
|
170
|
-
libp2pPort: 14003,
|
|
171
139
|
noUpnp: true,
|
|
172
140
|
openBrowser: false,
|
|
173
|
-
noInvite: true,
|
|
174
141
|
serviceHost: fake,
|
|
175
142
|
wizard: async (defaults) => {
|
|
176
143
|
seenDefaults = { ...defaults };
|
|
@@ -178,10 +145,8 @@ describe('Installer smoke', () => {
|
|
|
178
145
|
return {
|
|
179
146
|
dataDir: defaults.dataDir,
|
|
180
147
|
uiPort: defaults.uiPort,
|
|
181
|
-
libp2pPort: defaults.libp2pPort,
|
|
182
148
|
upnpEnabled: defaults.upnpEnabled,
|
|
183
149
|
configureDdns: false,
|
|
184
|
-
ownCadre: defaults.ownCadre,
|
|
185
150
|
};
|
|
186
151
|
},
|
|
187
152
|
});
|
|
@@ -189,9 +154,7 @@ describe('Installer smoke', () => {
|
|
|
189
154
|
expect(seenDefaults).toEqual({
|
|
190
155
|
dataDir: tmp,
|
|
191
156
|
uiPort: 19997,
|
|
192
|
-
libp2pPort: 14003,
|
|
193
157
|
upnpEnabled: false,
|
|
194
|
-
ownCadre: false,
|
|
195
158
|
});
|
|
196
159
|
expect(fake.installCalls).toHaveLength(1);
|
|
197
160
|
expect(fake.installCalls[0]!.dataDir).toBe(tmp);
|
|
@@ -205,59 +168,15 @@ describe('Installer smoke', () => {
|
|
|
205
168
|
await installer.install({
|
|
206
169
|
nonInteractive: false,
|
|
207
170
|
openBrowser: false,
|
|
208
|
-
noInvite: true,
|
|
209
171
|
serviceHost: fake,
|
|
210
172
|
wizard: async () => ({
|
|
211
173
|
dataDir: tmp,
|
|
212
174
|
uiPort: 19996,
|
|
213
|
-
libp2pPort: 14004,
|
|
214
175
|
upnpEnabled: true,
|
|
215
176
|
configureDdns: false,
|
|
216
|
-
ownCadre: false,
|
|
217
177
|
}),
|
|
218
178
|
});
|
|
219
179
|
expect(fake.installCalls).toHaveLength(1);
|
|
220
180
|
expect(fake.installCalls[0]!.dataDir).toBe(tmp);
|
|
221
181
|
});
|
|
222
|
-
|
|
223
|
-
it('writes ownCadre.enabled=false by default and true when opted in', async () => {
|
|
224
|
-
const { readHostConfig } = await import('../config.js');
|
|
225
|
-
|
|
226
|
-
// Default (donor-only): --own-cadre absent → ownCadre.enabled false.
|
|
227
|
-
const fakeA = new FakeServiceHost();
|
|
228
|
-
const installerA = new Installer({ platform: 'linux' });
|
|
229
|
-
await installerA.install({
|
|
230
|
-
nonInteractive: true, dataDir: tmp, openBrowser: false, noInvite: true, serviceHost: fakeA,
|
|
231
|
-
});
|
|
232
|
-
const donorCfg = readHostConfig(join(tmp, 'host.config.json'));
|
|
233
|
-
expect(donorCfg.ownCadre).toEqual({ enabled: false });
|
|
234
|
-
|
|
235
|
-
// Opt-in: ownCadre:true (the --own-cadre flag) → ownCadre.enabled true.
|
|
236
|
-
const tmp2 = mkdtempSync(join(tmpdir(), 'cadre-host-installer-owncadre-'));
|
|
237
|
-
try {
|
|
238
|
-
const fakeB = new FakeServiceHost();
|
|
239
|
-
const installerB = new Installer({ platform: 'linux' });
|
|
240
|
-
await installerB.install({
|
|
241
|
-
nonInteractive: true, dataDir: tmp2, ownCadre: true, openBrowser: false, noInvite: true, serviceHost: fakeB,
|
|
242
|
-
});
|
|
243
|
-
const founderCfg = readHostConfig(join(tmp2, 'host.config.json'));
|
|
244
|
-
expect(founderCfg.ownCadre).toEqual({ enabled: true });
|
|
245
|
-
} finally {
|
|
246
|
-
rmSync(tmp2, { recursive: true, force: true });
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it('re-running install preserves the existing identity', async () => {
|
|
251
|
-
const fake = new FakeServiceHost();
|
|
252
|
-
const installer = new Installer({ platform: 'linux' });
|
|
253
|
-
await installer.install({
|
|
254
|
-
nonInteractive: true, dataDir: tmp, openBrowser: false, noInvite: true, serviceHost: fake,
|
|
255
|
-
});
|
|
256
|
-
const idBefore = (await import('node:fs')).readFileSync(join(tmp, 'identity.key'));
|
|
257
|
-
await installer.install({
|
|
258
|
-
nonInteractive: true, dataDir: tmp, openBrowser: false, noInvite: true, serviceHost: fake,
|
|
259
|
-
});
|
|
260
|
-
const idAfter = (await import('node:fs')).readFileSync(join(tmp, 'identity.key'));
|
|
261
|
-
expect(Buffer.from(idBefore).equals(Buffer.from(idAfter))).toBe(true);
|
|
262
|
-
});
|
|
263
182
|
});
|
|
@@ -1,64 +1,56 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { runWizardWith, type WizardDefaults } from '../wizard.js';
|
|
4
|
-
|
|
5
|
-
const defaults: WizardDefaults = {
|
|
6
|
-
dataDir: '/data/cadre-host',
|
|
7
|
-
uiPort: 8765,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
'
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('rejects unrecognized yes/no answers', async () => {
|
|
60
|
-
await expect(
|
|
61
|
-
runWizardWith(defaults, scripted(['', '', '', 'maybe', ''])),
|
|
62
|
-
).rejects.toThrow(/Invalid yes\/no/);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { runWizardWith, type WizardDefaults } from '../wizard.js';
|
|
4
|
+
|
|
5
|
+
const defaults: WizardDefaults = {
|
|
6
|
+
dataDir: '/data/cadre-host',
|
|
7
|
+
uiPort: 8765,
|
|
8
|
+
upnpEnabled: true,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function scripted(answers: ReadonlyArray<string>): (label: string, fallback: string) => Promise<string> {
|
|
12
|
+
let i = 0;
|
|
13
|
+
return async (_label, _fallback) => {
|
|
14
|
+
if (i >= answers.length) throw new Error(`wizard asked more questions than scripted (i=${i})`);
|
|
15
|
+
return answers[i++]!;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('runWizardWith', () => {
|
|
20
|
+
it('returns the defaults when every answer is blank', async () => {
|
|
21
|
+
const out = await runWizardWith(defaults, scripted(['', '', '', '']));
|
|
22
|
+
expect(out).toEqual({
|
|
23
|
+
dataDir: '/data/cadre-host',
|
|
24
|
+
uiPort: 8765,
|
|
25
|
+
upnpEnabled: true,
|
|
26
|
+
configureDdns: false,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('respects overrides', async () => {
|
|
31
|
+
const out = await runWizardWith(defaults, scripted([
|
|
32
|
+
'/srv/host',
|
|
33
|
+
'9000',
|
|
34
|
+
'n',
|
|
35
|
+
'y',
|
|
36
|
+
]));
|
|
37
|
+
expect(out).toEqual({
|
|
38
|
+
dataDir: '/srv/host',
|
|
39
|
+
uiPort: 9000,
|
|
40
|
+
upnpEnabled: false,
|
|
41
|
+
configureDdns: true,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('rejects invalid port input', async () => {
|
|
46
|
+
await expect(
|
|
47
|
+
runWizardWith(defaults, scripted(['', 'banana', '', ''])),
|
|
48
|
+
).rejects.toThrow(/Invalid port/);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('rejects unrecognized yes/no answers', async () => {
|
|
52
|
+
await expect(
|
|
53
|
+
runWizardWith(defaults, scripted(['', '', 'maybe', ''])),
|
|
54
|
+
).rejects.toThrow(/Invalid yes\/no/);
|
|
55
|
+
});
|
|
56
|
+
});
|