@le-space/ui 0.3.6 → 0.3.8
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/package.json +3 -3
- package/react/index.js +304 -181
- package/shared/index.js +253 -179
- package/svelte/SponsorRelayFab.svelte +10 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@le-space/ui",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "Floating Aleph sponsor relay UI components for Svelte and React.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"access": "public"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@le-space/browser": "0.3.
|
|
40
|
-
"@le-space/core": "0.3.
|
|
39
|
+
"@le-space/browser": "0.3.8",
|
|
40
|
+
"@le-space/core": "0.3.8",
|
|
41
41
|
"viem": "^2.38.5"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
package/react/index.js
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
// src/react/SponsorRelayFab.tsx
|
|
2
2
|
import React from "react";
|
|
3
3
|
|
|
4
|
+
// package.json
|
|
5
|
+
var package_default = {
|
|
6
|
+
name: "@le-space/ui",
|
|
7
|
+
version: "0.3.8",
|
|
8
|
+
description: "Floating Aleph sponsor relay UI components for Svelte and React.",
|
|
9
|
+
license: "MIT",
|
|
10
|
+
type: "module",
|
|
11
|
+
main: "./src/shared/index.ts",
|
|
12
|
+
types: "./src/shared/index.ts",
|
|
13
|
+
dependencies: {
|
|
14
|
+
"@le-space/browser": "workspace:*",
|
|
15
|
+
"@le-space/core": "workspace:*",
|
|
16
|
+
viem: "^2.38.5"
|
|
17
|
+
},
|
|
18
|
+
peerDependencies: {
|
|
19
|
+
react: ">=18",
|
|
20
|
+
"react-dom": ">=18",
|
|
21
|
+
svelte: ">=4"
|
|
22
|
+
},
|
|
23
|
+
devDependencies: {
|
|
24
|
+
"@types/react": "^18.3.27",
|
|
25
|
+
"@types/react-dom": "^18.3.7"
|
|
26
|
+
},
|
|
27
|
+
scripts: {
|
|
28
|
+
build: 'echo "ui build placeholder"',
|
|
29
|
+
"build:publish": "tsup src/shared/index.ts --format esm --dts --clean --tsconfig tsconfig.publish.json --out-dir dist/shared && tsup src/react/index.ts --format esm --dts --tsconfig tsconfig.publish.json --out-dir dist/react",
|
|
30
|
+
test: "pnpm run build:publish && node --test ./test/**/*.test.mjs",
|
|
31
|
+
lint: 'echo "ui lint placeholder"'
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
4
35
|
// src/shared/constants.ts
|
|
5
36
|
var DEFAULT_INSTANCE_NAME = "sponsor-relay";
|
|
6
37
|
var DEFAULT_MANIFEST_URL = "./rootfs-manifest.json";
|
|
@@ -3646,158 +3677,156 @@ var SponsorRelayController = class {
|
|
|
3646
3677
|
this.state.pricingSummary.pricing,
|
|
3647
3678
|
this.state.pricingSummary.tier
|
|
3648
3679
|
);
|
|
3649
|
-
this.
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
});
|
|
3656
|
-
const content = createInstanceContent({
|
|
3657
|
-
address: this.state.wallet.address,
|
|
3658
|
-
name: this.state.instanceName.trim(),
|
|
3659
|
-
sshPublicKey: this.state.sshPublicKey.trim(),
|
|
3660
|
-
rootfsItemHash: this.state.manifest.rootfsItemHash,
|
|
3661
|
-
rootfsSizeMiB: Math.max(
|
|
3662
|
-
this.state.manifest.rootfsSizeMiB,
|
|
3663
|
-
spec.diskMiB
|
|
3664
|
-
),
|
|
3665
|
-
vcpus: spec.vcpus,
|
|
3666
|
-
memoryMiB: spec.memoryMiB,
|
|
3667
|
-
rootfsVersion: this.state.manifest.version,
|
|
3668
|
-
crnHash: this.state.selectedCrn?.hash
|
|
3669
|
-
});
|
|
3670
|
-
const result = await deployInstance({
|
|
3671
|
-
sender: this.state.wallet.address,
|
|
3672
|
-
content,
|
|
3673
|
-
hasher: sha256Hex,
|
|
3674
|
-
signer: personalSign,
|
|
3675
|
-
fetch: (url, init) => fetch(url, init),
|
|
3676
|
-
apiHost: this.client.apiHost,
|
|
3677
|
-
sync: false,
|
|
3678
|
-
onProgress: (event) => {
|
|
3679
|
-
this.emitProgress(event);
|
|
3680
|
-
}
|
|
3681
|
-
});
|
|
3682
|
-
this.patch({
|
|
3683
|
-
statusText: `Deployment submitted: ${result.itemHash}`,
|
|
3684
|
-
lastDeploymentHash: result.itemHash
|
|
3685
|
-
});
|
|
3686
|
-
this.trace("deploy:broadcasted", result);
|
|
3687
|
-
this.lastRuntimeReadyHash = null;
|
|
3688
|
-
const inspection = await waitForDeploymentResult2(result.itemHash, {
|
|
3689
|
-
rootfsRef: this.state.manifest.rootfsItemHash,
|
|
3690
|
-
apiHost: this.client.apiHost,
|
|
3691
|
-
fetch: (url, init) => fetch(url, init),
|
|
3692
|
-
attempts: UI_DEPLOY_WAIT_ATTEMPTS,
|
|
3693
|
-
delayMs: UI_DEPLOY_WAIT_DELAY_MS,
|
|
3694
|
-
onAttempt: (inspectionResult) => {
|
|
3695
|
-
if (inspectionResult.status === "processed") {
|
|
3696
|
-
this.emitProgress({
|
|
3697
|
-
stage: "deployment-confirmed",
|
|
3698
|
-
label: "Deployment accepted by Aleph",
|
|
3699
|
-
progress: 82,
|
|
3700
|
-
status: "success",
|
|
3701
|
-
itemHash: result.itemHash,
|
|
3702
|
-
detail: "Aleph processed the instance message."
|
|
3703
|
-
});
|
|
3704
|
-
return;
|
|
3705
|
-
}
|
|
3706
|
-
if (inspectionResult.status === "rejected") {
|
|
3707
|
-
this.emitProgress({
|
|
3708
|
-
stage: "deployment-rejected",
|
|
3709
|
-
label: "Deployment rejected",
|
|
3710
|
-
progress: 100,
|
|
3711
|
-
status: "error",
|
|
3712
|
-
itemHash: result.itemHash,
|
|
3713
|
-
detail: inspectionResult.rejectionReason ?? "Aleph rejected the deployment.",
|
|
3714
|
-
error: inspectionResult.rejectionReason ?? "Aleph rejected the deployment."
|
|
3715
|
-
});
|
|
3716
|
-
return;
|
|
3717
|
-
}
|
|
3718
|
-
this.emitProgress({
|
|
3719
|
-
stage: "waiting-for-aleph",
|
|
3720
|
-
label: "Waiting for Aleph",
|
|
3721
|
-
progress: 76,
|
|
3722
|
-
status: "info",
|
|
3723
|
-
itemHash: result.itemHash,
|
|
3724
|
-
detail: "Deployment submitted. Waiting for Aleph to process the instance message."
|
|
3725
|
-
});
|
|
3680
|
+
const compatibleCrns2 = compatibleCrnsForTier(this.state.crns, {
|
|
3681
|
+
...this.state,
|
|
3682
|
+
pricingSummary: {
|
|
3683
|
+
...this.state.pricingSummary,
|
|
3684
|
+
pricing: this.state.pricingSummary.pricing,
|
|
3685
|
+
tier: this.state.pricingSummary.tier
|
|
3726
3686
|
}
|
|
3727
3687
|
});
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
)
|
|
3688
|
+
const preferredCrnHash = this.state.selectedCrn?.hash ?? null;
|
|
3689
|
+
const orderedCrns = [
|
|
3690
|
+
...compatibleCrns2.filter((crn) => crn.hash === preferredCrnHash),
|
|
3691
|
+
...compatibleCrns2.filter((crn) => crn.hash !== preferredCrnHash)
|
|
3692
|
+
].slice(0, 5);
|
|
3693
|
+
if (orderedCrns.length === 0) {
|
|
3694
|
+
throw new Error("No compatible CRN is currently available for this tier.");
|
|
3732
3695
|
}
|
|
3733
|
-
|
|
3734
|
-
|
|
3696
|
+
const attemptErrors = [];
|
|
3697
|
+
let lastError = null;
|
|
3698
|
+
for (const [candidateIndex, candidateCrn] of orderedCrns.entries()) {
|
|
3699
|
+
let attemptItemHash = null;
|
|
3735
3700
|
this.emitProgress({
|
|
3736
|
-
stage: "
|
|
3737
|
-
label: "
|
|
3738
|
-
progress:
|
|
3701
|
+
stage: "selecting-crn",
|
|
3702
|
+
label: "Selecting CRN",
|
|
3703
|
+
progress: 18,
|
|
3739
3704
|
status: "info",
|
|
3740
|
-
|
|
3741
|
-
detail: "Publishing the required Aleph port-forward aggregate from the manifest."
|
|
3742
|
-
});
|
|
3743
|
-
await ensureInstancePortForwards({
|
|
3744
|
-
sender: this.state.wallet.address,
|
|
3745
|
-
instanceItemHash: result.itemHash,
|
|
3746
|
-
manifest: toSharedRootfsManifest(this.state.manifest),
|
|
3747
|
-
signer: personalSign,
|
|
3748
|
-
hasher: sha256Hex,
|
|
3749
|
-
fetch: (url, init) => fetch(url, init),
|
|
3750
|
-
apiHost: this.client.apiHost,
|
|
3751
|
-
sync: true
|
|
3752
|
-
});
|
|
3753
|
-
this.trace("deploy:port-forwards-published", {
|
|
3754
|
-
itemHash: result.itemHash,
|
|
3755
|
-
requiredPortForwards: this.state.manifest.requiredPortForwards
|
|
3705
|
+
detail: `Trying ${candidateCrn.name ?? candidateCrn.hash} (${candidateIndex + 1}/${orderedCrns.length})`
|
|
3756
3706
|
});
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3707
|
+
try {
|
|
3708
|
+
const content = createInstanceContent({
|
|
3709
|
+
address: this.state.wallet.address,
|
|
3710
|
+
name: this.state.instanceName.trim(),
|
|
3711
|
+
sshPublicKey: this.state.sshPublicKey.trim(),
|
|
3712
|
+
rootfsItemHash: this.state.manifest.rootfsItemHash,
|
|
3713
|
+
rootfsSizeMiB: Math.max(
|
|
3714
|
+
this.state.manifest.rootfsSizeMiB,
|
|
3715
|
+
spec.diskMiB
|
|
3716
|
+
),
|
|
3717
|
+
vcpus: spec.vcpus,
|
|
3718
|
+
memoryMiB: spec.memoryMiB,
|
|
3719
|
+
rootfsVersion: this.state.manifest.version,
|
|
3720
|
+
crnHash: candidateCrn.hash
|
|
3721
|
+
});
|
|
3722
|
+
const result = await deployInstance({
|
|
3723
|
+
sender: this.state.wallet.address,
|
|
3724
|
+
content,
|
|
3725
|
+
hasher: sha256Hex,
|
|
3726
|
+
signer: personalSign,
|
|
3727
|
+
fetch: (url, init) => fetch(url, init),
|
|
3728
|
+
apiHost: this.client.apiHost,
|
|
3729
|
+
sync: false,
|
|
3730
|
+
onProgress: (event) => {
|
|
3731
|
+
this.emitProgress(event);
|
|
3732
|
+
}
|
|
3733
|
+
});
|
|
3734
|
+
attemptItemHash = result.itemHash;
|
|
3735
|
+
this.patch({
|
|
3736
|
+
statusText: `Deployment submitted: ${result.itemHash}`,
|
|
3737
|
+
lastDeploymentHash: result.itemHash
|
|
3738
|
+
});
|
|
3739
|
+
this.trace("deploy:broadcasted", {
|
|
3740
|
+
...result,
|
|
3741
|
+
crnHash: candidateCrn.hash,
|
|
3742
|
+
crnName: candidateCrn.name
|
|
3743
|
+
});
|
|
3744
|
+
this.lastRuntimeReadyHash = null;
|
|
3745
|
+
const inspection = await waitForDeploymentResult2(result.itemHash, {
|
|
3746
|
+
rootfsRef: this.state.manifest.rootfsItemHash,
|
|
3747
|
+
apiHost: this.client.apiHost,
|
|
3748
|
+
fetch: (url, init) => fetch(url, init),
|
|
3749
|
+
attempts: UI_DEPLOY_WAIT_ATTEMPTS,
|
|
3750
|
+
delayMs: UI_DEPLOY_WAIT_DELAY_MS,
|
|
3751
|
+
onAttempt: (inspectionResult) => {
|
|
3752
|
+
if (inspectionResult.status === "processed") {
|
|
3753
|
+
this.emitProgress({
|
|
3754
|
+
stage: "deployment-confirmed",
|
|
3755
|
+
label: "Deployment accepted by Aleph",
|
|
3756
|
+
progress: 82,
|
|
3757
|
+
status: "success",
|
|
3758
|
+
itemHash: result.itemHash,
|
|
3759
|
+
detail: "Aleph processed the instance message."
|
|
3760
|
+
});
|
|
3761
|
+
return;
|
|
3762
|
+
}
|
|
3763
|
+
if (inspectionResult.status === "rejected") {
|
|
3764
|
+
this.emitProgress({
|
|
3765
|
+
stage: "deployment-rejected",
|
|
3766
|
+
label: "Deployment rejected",
|
|
3767
|
+
progress: 100,
|
|
3768
|
+
status: "error",
|
|
3769
|
+
itemHash: result.itemHash,
|
|
3770
|
+
detail: inspectionResult.rejectionReason ?? "Aleph rejected the deployment.",
|
|
3771
|
+
error: inspectionResult.rejectionReason ?? "Aleph rejected the deployment."
|
|
3772
|
+
});
|
|
3773
|
+
return;
|
|
3774
|
+
}
|
|
3775
|
+
this.emitProgress({
|
|
3776
|
+
stage: "waiting-for-aleph",
|
|
3777
|
+
label: "Waiting for Aleph",
|
|
3778
|
+
progress: 76,
|
|
3779
|
+
status: "info",
|
|
3780
|
+
itemHash: result.itemHash,
|
|
3781
|
+
detail: "Deployment submitted. Waiting for Aleph to process the instance message."
|
|
3782
|
+
});
|
|
3783
|
+
}
|
|
3784
|
+
});
|
|
3785
|
+
if (inspection.status !== "processed") {
|
|
3786
|
+
throw new Error(
|
|
3787
|
+
inspection.rejectionReason ?? `Deployment ${result.itemHash} stayed ${inspection.status} on Aleph.`
|
|
3788
|
+
);
|
|
3771
3789
|
}
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
this.emitProgress({
|
|
3775
|
-
stage: "deployment-confirmed",
|
|
3776
|
-
label: "Waiting for runtime",
|
|
3777
|
-
progress: 90,
|
|
3778
|
-
status: "warning",
|
|
3779
|
-
itemHash: result.itemHash,
|
|
3780
|
-
detail: "Aleph accepted the deployment. Waiting for runtime networking and mapped ports."
|
|
3781
|
-
});
|
|
3782
|
-
const runtime = await waitForVmRuntime({
|
|
3783
|
-
itemHash: result.itemHash,
|
|
3784
|
-
fetch: (url, init) => fetch(url, init),
|
|
3785
|
-
crnHash: this.state.selectedCrn?.hash,
|
|
3786
|
-
crns: this.state.crns,
|
|
3787
|
-
crnListUrl: this.props.crnListUrl,
|
|
3788
|
-
attempts: UI_RUNTIME_WAIT_ATTEMPTS,
|
|
3789
|
-
delayMs: UI_RUNTIME_WAIT_DELAY_MS,
|
|
3790
|
-
onAttempt: (runtime2) => {
|
|
3791
|
-
if (runtime2.hostIpv4 && Object.keys(runtime2.mappedPorts ?? {}).length > 0) {
|
|
3790
|
+
this.trace("deploy:aleph-processed", inspection);
|
|
3791
|
+
if ((this.state.manifest.requiredPortForwards?.length ?? 0) > 0) {
|
|
3792
3792
|
this.emitProgress({
|
|
3793
|
-
stage: "
|
|
3794
|
-
label: "
|
|
3795
|
-
progress:
|
|
3796
|
-
status: "
|
|
3793
|
+
stage: "refreshing-instances",
|
|
3794
|
+
label: "Publishing port forwards",
|
|
3795
|
+
progress: 86,
|
|
3796
|
+
status: "info",
|
|
3797
|
+
itemHash: result.itemHash,
|
|
3798
|
+
detail: "Publishing the required Aleph port-forward aggregate from the manifest."
|
|
3799
|
+
});
|
|
3800
|
+
await ensureInstancePortForwards({
|
|
3801
|
+
sender: this.state.wallet.address,
|
|
3802
|
+
instanceItemHash: result.itemHash,
|
|
3803
|
+
manifest: toSharedRootfsManifest(this.state.manifest),
|
|
3804
|
+
signer: personalSign,
|
|
3805
|
+
hasher: sha256Hex,
|
|
3806
|
+
fetch: (url, init) => fetch(url, init),
|
|
3807
|
+
apiHost: this.client.apiHost,
|
|
3808
|
+
sync: true
|
|
3809
|
+
});
|
|
3810
|
+
this.trace("deploy:port-forwards-published", {
|
|
3811
|
+
itemHash: result.itemHash,
|
|
3812
|
+
requiredPortForwards: this.state.manifest.requiredPortForwards
|
|
3813
|
+
});
|
|
3814
|
+
}
|
|
3815
|
+
if (candidateCrn.address) {
|
|
3816
|
+
this.trace("deploy:notifying-crn", {
|
|
3797
3817
|
itemHash: result.itemHash,
|
|
3798
|
-
|
|
3818
|
+
crnName: candidateCrn.name,
|
|
3819
|
+
crnHash: candidateCrn.hash,
|
|
3820
|
+
crnUrl: candidateCrn.address
|
|
3821
|
+
});
|
|
3822
|
+
await notifyCrnAllocationWithRetry({
|
|
3823
|
+
crnUrl: candidateCrn.address,
|
|
3824
|
+
itemHash: result.itemHash,
|
|
3825
|
+
fetch: (url, init) => fetch(url, init),
|
|
3826
|
+
onProgress: (event) => {
|
|
3827
|
+
this.emitProgress(event);
|
|
3828
|
+
}
|
|
3799
3829
|
});
|
|
3800
|
-
return;
|
|
3801
3830
|
}
|
|
3802
3831
|
this.emitProgress({
|
|
3803
3832
|
stage: "deployment-confirmed",
|
|
@@ -3805,46 +3834,122 @@ var SponsorRelayController = class {
|
|
|
3805
3834
|
progress: 90,
|
|
3806
3835
|
status: "warning",
|
|
3807
3836
|
itemHash: result.itemHash,
|
|
3808
|
-
detail:
|
|
3837
|
+
detail: "Aleph accepted the deployment. Waiting for runtime networking and mapped ports."
|
|
3838
|
+
});
|
|
3839
|
+
const runtime = await waitForVmRuntime({
|
|
3840
|
+
itemHash: result.itemHash,
|
|
3841
|
+
fetch: (url, init) => fetch(url, init),
|
|
3842
|
+
crnHash: candidateCrn.hash,
|
|
3843
|
+
crns: this.state.crns,
|
|
3844
|
+
crnListUrl: this.props.crnListUrl,
|
|
3845
|
+
attempts: UI_RUNTIME_WAIT_ATTEMPTS,
|
|
3846
|
+
delayMs: UI_RUNTIME_WAIT_DELAY_MS,
|
|
3847
|
+
onAttempt: (runtime2) => {
|
|
3848
|
+
if (runtime2.hostIpv4 && Object.keys(runtime2.mappedPorts ?? {}).length > 0) {
|
|
3849
|
+
this.emitProgress({
|
|
3850
|
+
stage: "completed",
|
|
3851
|
+
label: "Runtime ready",
|
|
3852
|
+
progress: 100,
|
|
3853
|
+
status: "success",
|
|
3854
|
+
itemHash: result.itemHash,
|
|
3855
|
+
detail: "Runtime networking and mapped ports are now available."
|
|
3856
|
+
});
|
|
3857
|
+
return;
|
|
3858
|
+
}
|
|
3859
|
+
this.emitProgress({
|
|
3860
|
+
stage: "deployment-confirmed",
|
|
3861
|
+
label: "Waiting for runtime",
|
|
3862
|
+
progress: 90,
|
|
3863
|
+
status: "warning",
|
|
3864
|
+
itemHash: result.itemHash,
|
|
3865
|
+
detail: runtime2.diagnostics?.reason ?? "Waiting for CRN runtime networking and mapped ports."
|
|
3866
|
+
});
|
|
3867
|
+
}
|
|
3868
|
+
});
|
|
3869
|
+
if (!runtime.hostIpv4 || Object.keys(runtime.mappedPorts ?? {}).length === 0) {
|
|
3870
|
+
throw new Error(
|
|
3871
|
+
runtime.diagnostics?.reason ?? "Deployment was processed, but runtime networking never exposed mapped ports."
|
|
3872
|
+
);
|
|
3873
|
+
}
|
|
3874
|
+
this.trace("deploy:runtime-ready", runtime);
|
|
3875
|
+
this.lastRuntimeReadyHash = result.itemHash;
|
|
3876
|
+
this.rememberRuntimeDetails(result.itemHash, {
|
|
3877
|
+
messageStatus: "processed",
|
|
3878
|
+
allocationSource: runtime.allocation?.source ?? null,
|
|
3879
|
+
crnUrl: runtime.allocation?.crnUrl ?? runtime.execution?.crnUrl ?? null,
|
|
3880
|
+
hostIpv4: runtime.hostIpv4 ?? null,
|
|
3881
|
+
ipv6: runtime.ipv6 ?? null,
|
|
3882
|
+
vmIpv4: runtime.execution?.networking?.ipv4_ip ?? null,
|
|
3883
|
+
webUrl: runtime.webAccessUrl ?? runtime.proxyUrl ?? runtime.execution?.networking?.proxy_url ?? null,
|
|
3884
|
+
sshCommand: runtime.sshCommand ?? null,
|
|
3885
|
+
mappedPorts: runtime.execution != null ? mappedPorts(runtime.execution) : runtimeMappedPorts(runtime.mappedPorts),
|
|
3886
|
+
execution: runtime.execution ?? null,
|
|
3887
|
+
error: null
|
|
3888
|
+
});
|
|
3889
|
+
await this.configureRelayBootstrapRegistration({
|
|
3890
|
+
itemHash: result.itemHash,
|
|
3891
|
+
runtime
|
|
3892
|
+
});
|
|
3893
|
+
this.patch({
|
|
3894
|
+
busy: { deploying: false }
|
|
3895
|
+
});
|
|
3896
|
+
this.emitProgress({
|
|
3897
|
+
stage: "refreshing-instances",
|
|
3898
|
+
label: "Refreshing instances",
|
|
3899
|
+
progress: 96,
|
|
3900
|
+
status: "info",
|
|
3901
|
+
itemHash: result.itemHash,
|
|
3902
|
+
detail: "Reloading deployments and runtime state."
|
|
3903
|
+
});
|
|
3904
|
+
await this.refresh();
|
|
3905
|
+
return;
|
|
3906
|
+
} catch (error) {
|
|
3907
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
3908
|
+
const attemptLabel = candidateCrn.name ?? candidateCrn.hash;
|
|
3909
|
+
attemptErrors.push(`${attemptLabel}: ${lastError.message}`);
|
|
3910
|
+
this.trace("deploy:crn-attempt-error", {
|
|
3911
|
+
crnHash: candidateCrn.hash,
|
|
3912
|
+
crnName: candidateCrn.name,
|
|
3913
|
+
itemHash: attemptItemHash,
|
|
3914
|
+
error: lastError.message
|
|
3809
3915
|
});
|
|
3916
|
+
if (attemptItemHash) {
|
|
3917
|
+
await forgetAlephMessages({
|
|
3918
|
+
sender: this.state.wallet.address,
|
|
3919
|
+
hashes: [attemptItemHash],
|
|
3920
|
+
reason: `Discard failed Sponsor Relay deployment attempt on ${attemptLabel}`,
|
|
3921
|
+
signer: personalSign,
|
|
3922
|
+
hasher: sha256Hex,
|
|
3923
|
+
fetch: (url, init) => fetch(url, init).then(async (response) => ({
|
|
3924
|
+
ok: response.ok,
|
|
3925
|
+
status: response.status,
|
|
3926
|
+
json: async () => await response.json()
|
|
3927
|
+
})),
|
|
3928
|
+
apiHost: this.client.apiHost
|
|
3929
|
+
}).catch((cleanupError) => {
|
|
3930
|
+
this.trace("deploy:cleanup-error", {
|
|
3931
|
+
itemHash: attemptItemHash,
|
|
3932
|
+
error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
|
|
3933
|
+
});
|
|
3934
|
+
});
|
|
3935
|
+
}
|
|
3936
|
+
if (candidateIndex < orderedCrns.length - 1) {
|
|
3937
|
+
this.emitProgress({
|
|
3938
|
+
stage: "selecting-crn",
|
|
3939
|
+
label: "Retrying another CRN",
|
|
3940
|
+
progress: 20,
|
|
3941
|
+
status: "warning",
|
|
3942
|
+
itemHash: attemptItemHash,
|
|
3943
|
+
detail: `${attemptLabel} failed: ${lastError.message}`,
|
|
3944
|
+
error: lastError.message
|
|
3945
|
+
});
|
|
3946
|
+
continue;
|
|
3947
|
+
}
|
|
3810
3948
|
}
|
|
3811
|
-
});
|
|
3812
|
-
if (!runtime.hostIpv4 || Object.keys(runtime.mappedPorts ?? {}).length === 0) {
|
|
3813
|
-
throw new Error(
|
|
3814
|
-
runtime.diagnostics?.reason ?? "Deployment was processed, but runtime networking never exposed mapped ports."
|
|
3815
|
-
);
|
|
3816
3949
|
}
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
messageStatus: "processed",
|
|
3821
|
-
allocationSource: runtime.allocation?.source ?? null,
|
|
3822
|
-
crnUrl: runtime.allocation?.crnUrl ?? runtime.execution?.crnUrl ?? null,
|
|
3823
|
-
hostIpv4: runtime.hostIpv4 ?? null,
|
|
3824
|
-
ipv6: runtime.ipv6 ?? null,
|
|
3825
|
-
vmIpv4: runtime.execution?.networking?.ipv4_ip ?? null,
|
|
3826
|
-
webUrl: runtime.webAccessUrl ?? runtime.proxyUrl ?? runtime.execution?.networking?.proxy_url ?? null,
|
|
3827
|
-
sshCommand: runtime.sshCommand ?? null,
|
|
3828
|
-
mappedPorts: runtime.execution != null ? mappedPorts(runtime.execution) : runtimeMappedPorts(runtime.mappedPorts),
|
|
3829
|
-
execution: runtime.execution ?? null,
|
|
3830
|
-
error: null
|
|
3831
|
-
});
|
|
3832
|
-
await this.configureRelayBootstrapRegistration({
|
|
3833
|
-
itemHash: result.itemHash,
|
|
3834
|
-
runtime
|
|
3835
|
-
});
|
|
3836
|
-
this.patch({
|
|
3837
|
-
busy: { deploying: false }
|
|
3838
|
-
});
|
|
3839
|
-
this.emitProgress({
|
|
3840
|
-
stage: "refreshing-instances",
|
|
3841
|
-
label: "Refreshing instances",
|
|
3842
|
-
progress: 96,
|
|
3843
|
-
status: "info",
|
|
3844
|
-
itemHash: result.itemHash,
|
|
3845
|
-
detail: "Reloading deployments and runtime state."
|
|
3846
|
-
});
|
|
3847
|
-
await this.refresh();
|
|
3950
|
+
throw new Error(
|
|
3951
|
+
attemptErrors.length > 0 ? `All compatible CRNs failed. ${attemptErrors.join(" | ")}` : lastError?.message ?? "All compatible CRNs failed."
|
|
3952
|
+
);
|
|
3848
3953
|
} catch (error) {
|
|
3849
3954
|
this.trace("deploy:error", error);
|
|
3850
3955
|
this.patch({
|
|
@@ -4103,6 +4208,7 @@ function launcherIndicator(state) {
|
|
|
4103
4208
|
}
|
|
4104
4209
|
function SponsorRelayFab(props) {
|
|
4105
4210
|
const { controller, state } = useSponsorRelayController(props);
|
|
4211
|
+
const versionLabel = `v${package_default.version}`;
|
|
4106
4212
|
const launcherMode = props.launcherMode ?? "floating";
|
|
4107
4213
|
const indicator = launcherIndicator(state);
|
|
4108
4214
|
const [successFlash, setSuccessFlash] = React.useState(false);
|
|
@@ -4333,16 +4439,33 @@ function SponsorRelayFab(props) {
|
|
|
4333
4439
|
},
|
|
4334
4440
|
children: [
|
|
4335
4441
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
4336
|
-
/* @__PURE__ */
|
|
4442
|
+
/* @__PURE__ */ jsxs(
|
|
4337
4443
|
"div",
|
|
4338
4444
|
{
|
|
4339
4445
|
style: {
|
|
4446
|
+
display: "flex",
|
|
4447
|
+
alignItems: "center",
|
|
4448
|
+
gap: "0.45rem",
|
|
4340
4449
|
color: "#9fb2ca",
|
|
4341
4450
|
fontSize: "0.72rem",
|
|
4342
4451
|
textTransform: "uppercase",
|
|
4343
4452
|
letterSpacing: "0.08em"
|
|
4344
4453
|
},
|
|
4345
|
-
children:
|
|
4454
|
+
children: [
|
|
4455
|
+
/* @__PURE__ */ jsx("span", { children: "Aleph VM credit deployer" }),
|
|
4456
|
+
/* @__PURE__ */ jsx(
|
|
4457
|
+
"span",
|
|
4458
|
+
{
|
|
4459
|
+
style: {
|
|
4460
|
+
fontSize: "0.62rem",
|
|
4461
|
+
letterSpacing: "0.04em",
|
|
4462
|
+
textTransform: "none",
|
|
4463
|
+
color: "rgba(191, 219, 254, 0.82)"
|
|
4464
|
+
},
|
|
4465
|
+
children: versionLabel
|
|
4466
|
+
}
|
|
4467
|
+
)
|
|
4468
|
+
]
|
|
4346
4469
|
}
|
|
4347
4470
|
),
|
|
4348
4471
|
/* @__PURE__ */ jsx(
|
package/shared/index.js
CHANGED
|
@@ -3645,158 +3645,156 @@ var SponsorRelayController = class {
|
|
|
3645
3645
|
this.state.pricingSummary.pricing,
|
|
3646
3646
|
this.state.pricingSummary.tier
|
|
3647
3647
|
);
|
|
3648
|
-
this.
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
});
|
|
3655
|
-
const content = createInstanceContent({
|
|
3656
|
-
address: this.state.wallet.address,
|
|
3657
|
-
name: this.state.instanceName.trim(),
|
|
3658
|
-
sshPublicKey: this.state.sshPublicKey.trim(),
|
|
3659
|
-
rootfsItemHash: this.state.manifest.rootfsItemHash,
|
|
3660
|
-
rootfsSizeMiB: Math.max(
|
|
3661
|
-
this.state.manifest.rootfsSizeMiB,
|
|
3662
|
-
spec.diskMiB
|
|
3663
|
-
),
|
|
3664
|
-
vcpus: spec.vcpus,
|
|
3665
|
-
memoryMiB: spec.memoryMiB,
|
|
3666
|
-
rootfsVersion: this.state.manifest.version,
|
|
3667
|
-
crnHash: this.state.selectedCrn?.hash
|
|
3668
|
-
});
|
|
3669
|
-
const result = await deployInstance({
|
|
3670
|
-
sender: this.state.wallet.address,
|
|
3671
|
-
content,
|
|
3672
|
-
hasher: sha256Hex,
|
|
3673
|
-
signer: personalSign,
|
|
3674
|
-
fetch: (url, init) => fetch(url, init),
|
|
3675
|
-
apiHost: this.client.apiHost,
|
|
3676
|
-
sync: false,
|
|
3677
|
-
onProgress: (event) => {
|
|
3678
|
-
this.emitProgress(event);
|
|
3679
|
-
}
|
|
3680
|
-
});
|
|
3681
|
-
this.patch({
|
|
3682
|
-
statusText: `Deployment submitted: ${result.itemHash}`,
|
|
3683
|
-
lastDeploymentHash: result.itemHash
|
|
3684
|
-
});
|
|
3685
|
-
this.trace("deploy:broadcasted", result);
|
|
3686
|
-
this.lastRuntimeReadyHash = null;
|
|
3687
|
-
const inspection = await waitForDeploymentResult2(result.itemHash, {
|
|
3688
|
-
rootfsRef: this.state.manifest.rootfsItemHash,
|
|
3689
|
-
apiHost: this.client.apiHost,
|
|
3690
|
-
fetch: (url, init) => fetch(url, init),
|
|
3691
|
-
attempts: UI_DEPLOY_WAIT_ATTEMPTS,
|
|
3692
|
-
delayMs: UI_DEPLOY_WAIT_DELAY_MS,
|
|
3693
|
-
onAttempt: (inspectionResult) => {
|
|
3694
|
-
if (inspectionResult.status === "processed") {
|
|
3695
|
-
this.emitProgress({
|
|
3696
|
-
stage: "deployment-confirmed",
|
|
3697
|
-
label: "Deployment accepted by Aleph",
|
|
3698
|
-
progress: 82,
|
|
3699
|
-
status: "success",
|
|
3700
|
-
itemHash: result.itemHash,
|
|
3701
|
-
detail: "Aleph processed the instance message."
|
|
3702
|
-
});
|
|
3703
|
-
return;
|
|
3704
|
-
}
|
|
3705
|
-
if (inspectionResult.status === "rejected") {
|
|
3706
|
-
this.emitProgress({
|
|
3707
|
-
stage: "deployment-rejected",
|
|
3708
|
-
label: "Deployment rejected",
|
|
3709
|
-
progress: 100,
|
|
3710
|
-
status: "error",
|
|
3711
|
-
itemHash: result.itemHash,
|
|
3712
|
-
detail: inspectionResult.rejectionReason ?? "Aleph rejected the deployment.",
|
|
3713
|
-
error: inspectionResult.rejectionReason ?? "Aleph rejected the deployment."
|
|
3714
|
-
});
|
|
3715
|
-
return;
|
|
3716
|
-
}
|
|
3717
|
-
this.emitProgress({
|
|
3718
|
-
stage: "waiting-for-aleph",
|
|
3719
|
-
label: "Waiting for Aleph",
|
|
3720
|
-
progress: 76,
|
|
3721
|
-
status: "info",
|
|
3722
|
-
itemHash: result.itemHash,
|
|
3723
|
-
detail: "Deployment submitted. Waiting for Aleph to process the instance message."
|
|
3724
|
-
});
|
|
3648
|
+
const compatibleCrns2 = compatibleCrnsForTier(this.state.crns, {
|
|
3649
|
+
...this.state,
|
|
3650
|
+
pricingSummary: {
|
|
3651
|
+
...this.state.pricingSummary,
|
|
3652
|
+
pricing: this.state.pricingSummary.pricing,
|
|
3653
|
+
tier: this.state.pricingSummary.tier
|
|
3725
3654
|
}
|
|
3726
3655
|
});
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
)
|
|
3656
|
+
const preferredCrnHash = this.state.selectedCrn?.hash ?? null;
|
|
3657
|
+
const orderedCrns = [
|
|
3658
|
+
...compatibleCrns2.filter((crn) => crn.hash === preferredCrnHash),
|
|
3659
|
+
...compatibleCrns2.filter((crn) => crn.hash !== preferredCrnHash)
|
|
3660
|
+
].slice(0, 5);
|
|
3661
|
+
if (orderedCrns.length === 0) {
|
|
3662
|
+
throw new Error("No compatible CRN is currently available for this tier.");
|
|
3731
3663
|
}
|
|
3732
|
-
|
|
3733
|
-
|
|
3664
|
+
const attemptErrors = [];
|
|
3665
|
+
let lastError = null;
|
|
3666
|
+
for (const [candidateIndex, candidateCrn] of orderedCrns.entries()) {
|
|
3667
|
+
let attemptItemHash = null;
|
|
3734
3668
|
this.emitProgress({
|
|
3735
|
-
stage: "
|
|
3736
|
-
label: "
|
|
3737
|
-
progress:
|
|
3669
|
+
stage: "selecting-crn",
|
|
3670
|
+
label: "Selecting CRN",
|
|
3671
|
+
progress: 18,
|
|
3738
3672
|
status: "info",
|
|
3739
|
-
|
|
3740
|
-
detail: "Publishing the required Aleph port-forward aggregate from the manifest."
|
|
3741
|
-
});
|
|
3742
|
-
await ensureInstancePortForwards({
|
|
3743
|
-
sender: this.state.wallet.address,
|
|
3744
|
-
instanceItemHash: result.itemHash,
|
|
3745
|
-
manifest: toSharedRootfsManifest(this.state.manifest),
|
|
3746
|
-
signer: personalSign,
|
|
3747
|
-
hasher: sha256Hex,
|
|
3748
|
-
fetch: (url, init) => fetch(url, init),
|
|
3749
|
-
apiHost: this.client.apiHost,
|
|
3750
|
-
sync: true
|
|
3751
|
-
});
|
|
3752
|
-
this.trace("deploy:port-forwards-published", {
|
|
3753
|
-
itemHash: result.itemHash,
|
|
3754
|
-
requiredPortForwards: this.state.manifest.requiredPortForwards
|
|
3673
|
+
detail: `Trying ${candidateCrn.name ?? candidateCrn.hash} (${candidateIndex + 1}/${orderedCrns.length})`
|
|
3755
3674
|
});
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3675
|
+
try {
|
|
3676
|
+
const content = createInstanceContent({
|
|
3677
|
+
address: this.state.wallet.address,
|
|
3678
|
+
name: this.state.instanceName.trim(),
|
|
3679
|
+
sshPublicKey: this.state.sshPublicKey.trim(),
|
|
3680
|
+
rootfsItemHash: this.state.manifest.rootfsItemHash,
|
|
3681
|
+
rootfsSizeMiB: Math.max(
|
|
3682
|
+
this.state.manifest.rootfsSizeMiB,
|
|
3683
|
+
spec.diskMiB
|
|
3684
|
+
),
|
|
3685
|
+
vcpus: spec.vcpus,
|
|
3686
|
+
memoryMiB: spec.memoryMiB,
|
|
3687
|
+
rootfsVersion: this.state.manifest.version,
|
|
3688
|
+
crnHash: candidateCrn.hash
|
|
3689
|
+
});
|
|
3690
|
+
const result = await deployInstance({
|
|
3691
|
+
sender: this.state.wallet.address,
|
|
3692
|
+
content,
|
|
3693
|
+
hasher: sha256Hex,
|
|
3694
|
+
signer: personalSign,
|
|
3695
|
+
fetch: (url, init) => fetch(url, init),
|
|
3696
|
+
apiHost: this.client.apiHost,
|
|
3697
|
+
sync: false,
|
|
3698
|
+
onProgress: (event) => {
|
|
3699
|
+
this.emitProgress(event);
|
|
3700
|
+
}
|
|
3701
|
+
});
|
|
3702
|
+
attemptItemHash = result.itemHash;
|
|
3703
|
+
this.patch({
|
|
3704
|
+
statusText: `Deployment submitted: ${result.itemHash}`,
|
|
3705
|
+
lastDeploymentHash: result.itemHash
|
|
3706
|
+
});
|
|
3707
|
+
this.trace("deploy:broadcasted", {
|
|
3708
|
+
...result,
|
|
3709
|
+
crnHash: candidateCrn.hash,
|
|
3710
|
+
crnName: candidateCrn.name
|
|
3711
|
+
});
|
|
3712
|
+
this.lastRuntimeReadyHash = null;
|
|
3713
|
+
const inspection = await waitForDeploymentResult2(result.itemHash, {
|
|
3714
|
+
rootfsRef: this.state.manifest.rootfsItemHash,
|
|
3715
|
+
apiHost: this.client.apiHost,
|
|
3716
|
+
fetch: (url, init) => fetch(url, init),
|
|
3717
|
+
attempts: UI_DEPLOY_WAIT_ATTEMPTS,
|
|
3718
|
+
delayMs: UI_DEPLOY_WAIT_DELAY_MS,
|
|
3719
|
+
onAttempt: (inspectionResult) => {
|
|
3720
|
+
if (inspectionResult.status === "processed") {
|
|
3721
|
+
this.emitProgress({
|
|
3722
|
+
stage: "deployment-confirmed",
|
|
3723
|
+
label: "Deployment accepted by Aleph",
|
|
3724
|
+
progress: 82,
|
|
3725
|
+
status: "success",
|
|
3726
|
+
itemHash: result.itemHash,
|
|
3727
|
+
detail: "Aleph processed the instance message."
|
|
3728
|
+
});
|
|
3729
|
+
return;
|
|
3730
|
+
}
|
|
3731
|
+
if (inspectionResult.status === "rejected") {
|
|
3732
|
+
this.emitProgress({
|
|
3733
|
+
stage: "deployment-rejected",
|
|
3734
|
+
label: "Deployment rejected",
|
|
3735
|
+
progress: 100,
|
|
3736
|
+
status: "error",
|
|
3737
|
+
itemHash: result.itemHash,
|
|
3738
|
+
detail: inspectionResult.rejectionReason ?? "Aleph rejected the deployment.",
|
|
3739
|
+
error: inspectionResult.rejectionReason ?? "Aleph rejected the deployment."
|
|
3740
|
+
});
|
|
3741
|
+
return;
|
|
3742
|
+
}
|
|
3743
|
+
this.emitProgress({
|
|
3744
|
+
stage: "waiting-for-aleph",
|
|
3745
|
+
label: "Waiting for Aleph",
|
|
3746
|
+
progress: 76,
|
|
3747
|
+
status: "info",
|
|
3748
|
+
itemHash: result.itemHash,
|
|
3749
|
+
detail: "Deployment submitted. Waiting for Aleph to process the instance message."
|
|
3750
|
+
});
|
|
3751
|
+
}
|
|
3752
|
+
});
|
|
3753
|
+
if (inspection.status !== "processed") {
|
|
3754
|
+
throw new Error(
|
|
3755
|
+
inspection.rejectionReason ?? `Deployment ${result.itemHash} stayed ${inspection.status} on Aleph.`
|
|
3756
|
+
);
|
|
3770
3757
|
}
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
this.emitProgress({
|
|
3774
|
-
stage: "deployment-confirmed",
|
|
3775
|
-
label: "Waiting for runtime",
|
|
3776
|
-
progress: 90,
|
|
3777
|
-
status: "warning",
|
|
3778
|
-
itemHash: result.itemHash,
|
|
3779
|
-
detail: "Aleph accepted the deployment. Waiting for runtime networking and mapped ports."
|
|
3780
|
-
});
|
|
3781
|
-
const runtime = await waitForVmRuntime({
|
|
3782
|
-
itemHash: result.itemHash,
|
|
3783
|
-
fetch: (url, init) => fetch(url, init),
|
|
3784
|
-
crnHash: this.state.selectedCrn?.hash,
|
|
3785
|
-
crns: this.state.crns,
|
|
3786
|
-
crnListUrl: this.props.crnListUrl,
|
|
3787
|
-
attempts: UI_RUNTIME_WAIT_ATTEMPTS,
|
|
3788
|
-
delayMs: UI_RUNTIME_WAIT_DELAY_MS,
|
|
3789
|
-
onAttempt: (runtime2) => {
|
|
3790
|
-
if (runtime2.hostIpv4 && Object.keys(runtime2.mappedPorts ?? {}).length > 0) {
|
|
3758
|
+
this.trace("deploy:aleph-processed", inspection);
|
|
3759
|
+
if ((this.state.manifest.requiredPortForwards?.length ?? 0) > 0) {
|
|
3791
3760
|
this.emitProgress({
|
|
3792
|
-
stage: "
|
|
3793
|
-
label: "
|
|
3794
|
-
progress:
|
|
3795
|
-
status: "
|
|
3761
|
+
stage: "refreshing-instances",
|
|
3762
|
+
label: "Publishing port forwards",
|
|
3763
|
+
progress: 86,
|
|
3764
|
+
status: "info",
|
|
3765
|
+
itemHash: result.itemHash,
|
|
3766
|
+
detail: "Publishing the required Aleph port-forward aggregate from the manifest."
|
|
3767
|
+
});
|
|
3768
|
+
await ensureInstancePortForwards({
|
|
3769
|
+
sender: this.state.wallet.address,
|
|
3770
|
+
instanceItemHash: result.itemHash,
|
|
3771
|
+
manifest: toSharedRootfsManifest(this.state.manifest),
|
|
3772
|
+
signer: personalSign,
|
|
3773
|
+
hasher: sha256Hex,
|
|
3774
|
+
fetch: (url, init) => fetch(url, init),
|
|
3775
|
+
apiHost: this.client.apiHost,
|
|
3776
|
+
sync: true
|
|
3777
|
+
});
|
|
3778
|
+
this.trace("deploy:port-forwards-published", {
|
|
3779
|
+
itemHash: result.itemHash,
|
|
3780
|
+
requiredPortForwards: this.state.manifest.requiredPortForwards
|
|
3781
|
+
});
|
|
3782
|
+
}
|
|
3783
|
+
if (candidateCrn.address) {
|
|
3784
|
+
this.trace("deploy:notifying-crn", {
|
|
3785
|
+
itemHash: result.itemHash,
|
|
3786
|
+
crnName: candidateCrn.name,
|
|
3787
|
+
crnHash: candidateCrn.hash,
|
|
3788
|
+
crnUrl: candidateCrn.address
|
|
3789
|
+
});
|
|
3790
|
+
await notifyCrnAllocationWithRetry({
|
|
3791
|
+
crnUrl: candidateCrn.address,
|
|
3796
3792
|
itemHash: result.itemHash,
|
|
3797
|
-
|
|
3793
|
+
fetch: (url, init) => fetch(url, init),
|
|
3794
|
+
onProgress: (event) => {
|
|
3795
|
+
this.emitProgress(event);
|
|
3796
|
+
}
|
|
3798
3797
|
});
|
|
3799
|
-
return;
|
|
3800
3798
|
}
|
|
3801
3799
|
this.emitProgress({
|
|
3802
3800
|
stage: "deployment-confirmed",
|
|
@@ -3804,46 +3802,122 @@ var SponsorRelayController = class {
|
|
|
3804
3802
|
progress: 90,
|
|
3805
3803
|
status: "warning",
|
|
3806
3804
|
itemHash: result.itemHash,
|
|
3807
|
-
detail:
|
|
3805
|
+
detail: "Aleph accepted the deployment. Waiting for runtime networking and mapped ports."
|
|
3806
|
+
});
|
|
3807
|
+
const runtime = await waitForVmRuntime({
|
|
3808
|
+
itemHash: result.itemHash,
|
|
3809
|
+
fetch: (url, init) => fetch(url, init),
|
|
3810
|
+
crnHash: candidateCrn.hash,
|
|
3811
|
+
crns: this.state.crns,
|
|
3812
|
+
crnListUrl: this.props.crnListUrl,
|
|
3813
|
+
attempts: UI_RUNTIME_WAIT_ATTEMPTS,
|
|
3814
|
+
delayMs: UI_RUNTIME_WAIT_DELAY_MS,
|
|
3815
|
+
onAttempt: (runtime2) => {
|
|
3816
|
+
if (runtime2.hostIpv4 && Object.keys(runtime2.mappedPorts ?? {}).length > 0) {
|
|
3817
|
+
this.emitProgress({
|
|
3818
|
+
stage: "completed",
|
|
3819
|
+
label: "Runtime ready",
|
|
3820
|
+
progress: 100,
|
|
3821
|
+
status: "success",
|
|
3822
|
+
itemHash: result.itemHash,
|
|
3823
|
+
detail: "Runtime networking and mapped ports are now available."
|
|
3824
|
+
});
|
|
3825
|
+
return;
|
|
3826
|
+
}
|
|
3827
|
+
this.emitProgress({
|
|
3828
|
+
stage: "deployment-confirmed",
|
|
3829
|
+
label: "Waiting for runtime",
|
|
3830
|
+
progress: 90,
|
|
3831
|
+
status: "warning",
|
|
3832
|
+
itemHash: result.itemHash,
|
|
3833
|
+
detail: runtime2.diagnostics?.reason ?? "Waiting for CRN runtime networking and mapped ports."
|
|
3834
|
+
});
|
|
3835
|
+
}
|
|
3836
|
+
});
|
|
3837
|
+
if (!runtime.hostIpv4 || Object.keys(runtime.mappedPorts ?? {}).length === 0) {
|
|
3838
|
+
throw new Error(
|
|
3839
|
+
runtime.diagnostics?.reason ?? "Deployment was processed, but runtime networking never exposed mapped ports."
|
|
3840
|
+
);
|
|
3841
|
+
}
|
|
3842
|
+
this.trace("deploy:runtime-ready", runtime);
|
|
3843
|
+
this.lastRuntimeReadyHash = result.itemHash;
|
|
3844
|
+
this.rememberRuntimeDetails(result.itemHash, {
|
|
3845
|
+
messageStatus: "processed",
|
|
3846
|
+
allocationSource: runtime.allocation?.source ?? null,
|
|
3847
|
+
crnUrl: runtime.allocation?.crnUrl ?? runtime.execution?.crnUrl ?? null,
|
|
3848
|
+
hostIpv4: runtime.hostIpv4 ?? null,
|
|
3849
|
+
ipv6: runtime.ipv6 ?? null,
|
|
3850
|
+
vmIpv4: runtime.execution?.networking?.ipv4_ip ?? null,
|
|
3851
|
+
webUrl: runtime.webAccessUrl ?? runtime.proxyUrl ?? runtime.execution?.networking?.proxy_url ?? null,
|
|
3852
|
+
sshCommand: runtime.sshCommand ?? null,
|
|
3853
|
+
mappedPorts: runtime.execution != null ? mappedPorts(runtime.execution) : runtimeMappedPorts(runtime.mappedPorts),
|
|
3854
|
+
execution: runtime.execution ?? null,
|
|
3855
|
+
error: null
|
|
3856
|
+
});
|
|
3857
|
+
await this.configureRelayBootstrapRegistration({
|
|
3858
|
+
itemHash: result.itemHash,
|
|
3859
|
+
runtime
|
|
3860
|
+
});
|
|
3861
|
+
this.patch({
|
|
3862
|
+
busy: { deploying: false }
|
|
3863
|
+
});
|
|
3864
|
+
this.emitProgress({
|
|
3865
|
+
stage: "refreshing-instances",
|
|
3866
|
+
label: "Refreshing instances",
|
|
3867
|
+
progress: 96,
|
|
3868
|
+
status: "info",
|
|
3869
|
+
itemHash: result.itemHash,
|
|
3870
|
+
detail: "Reloading deployments and runtime state."
|
|
3808
3871
|
});
|
|
3872
|
+
await this.refresh();
|
|
3873
|
+
return;
|
|
3874
|
+
} catch (error) {
|
|
3875
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
3876
|
+
const attemptLabel = candidateCrn.name ?? candidateCrn.hash;
|
|
3877
|
+
attemptErrors.push(`${attemptLabel}: ${lastError.message}`);
|
|
3878
|
+
this.trace("deploy:crn-attempt-error", {
|
|
3879
|
+
crnHash: candidateCrn.hash,
|
|
3880
|
+
crnName: candidateCrn.name,
|
|
3881
|
+
itemHash: attemptItemHash,
|
|
3882
|
+
error: lastError.message
|
|
3883
|
+
});
|
|
3884
|
+
if (attemptItemHash) {
|
|
3885
|
+
await forgetAlephMessages({
|
|
3886
|
+
sender: this.state.wallet.address,
|
|
3887
|
+
hashes: [attemptItemHash],
|
|
3888
|
+
reason: `Discard failed Sponsor Relay deployment attempt on ${attemptLabel}`,
|
|
3889
|
+
signer: personalSign,
|
|
3890
|
+
hasher: sha256Hex,
|
|
3891
|
+
fetch: (url, init) => fetch(url, init).then(async (response) => ({
|
|
3892
|
+
ok: response.ok,
|
|
3893
|
+
status: response.status,
|
|
3894
|
+
json: async () => await response.json()
|
|
3895
|
+
})),
|
|
3896
|
+
apiHost: this.client.apiHost
|
|
3897
|
+
}).catch((cleanupError) => {
|
|
3898
|
+
this.trace("deploy:cleanup-error", {
|
|
3899
|
+
itemHash: attemptItemHash,
|
|
3900
|
+
error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
|
|
3901
|
+
});
|
|
3902
|
+
});
|
|
3903
|
+
}
|
|
3904
|
+
if (candidateIndex < orderedCrns.length - 1) {
|
|
3905
|
+
this.emitProgress({
|
|
3906
|
+
stage: "selecting-crn",
|
|
3907
|
+
label: "Retrying another CRN",
|
|
3908
|
+
progress: 20,
|
|
3909
|
+
status: "warning",
|
|
3910
|
+
itemHash: attemptItemHash,
|
|
3911
|
+
detail: `${attemptLabel} failed: ${lastError.message}`,
|
|
3912
|
+
error: lastError.message
|
|
3913
|
+
});
|
|
3914
|
+
continue;
|
|
3915
|
+
}
|
|
3809
3916
|
}
|
|
3810
|
-
});
|
|
3811
|
-
if (!runtime.hostIpv4 || Object.keys(runtime.mappedPorts ?? {}).length === 0) {
|
|
3812
|
-
throw new Error(
|
|
3813
|
-
runtime.diagnostics?.reason ?? "Deployment was processed, but runtime networking never exposed mapped ports."
|
|
3814
|
-
);
|
|
3815
3917
|
}
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
messageStatus: "processed",
|
|
3820
|
-
allocationSource: runtime.allocation?.source ?? null,
|
|
3821
|
-
crnUrl: runtime.allocation?.crnUrl ?? runtime.execution?.crnUrl ?? null,
|
|
3822
|
-
hostIpv4: runtime.hostIpv4 ?? null,
|
|
3823
|
-
ipv6: runtime.ipv6 ?? null,
|
|
3824
|
-
vmIpv4: runtime.execution?.networking?.ipv4_ip ?? null,
|
|
3825
|
-
webUrl: runtime.webAccessUrl ?? runtime.proxyUrl ?? runtime.execution?.networking?.proxy_url ?? null,
|
|
3826
|
-
sshCommand: runtime.sshCommand ?? null,
|
|
3827
|
-
mappedPorts: runtime.execution != null ? mappedPorts(runtime.execution) : runtimeMappedPorts(runtime.mappedPorts),
|
|
3828
|
-
execution: runtime.execution ?? null,
|
|
3829
|
-
error: null
|
|
3830
|
-
});
|
|
3831
|
-
await this.configureRelayBootstrapRegistration({
|
|
3832
|
-
itemHash: result.itemHash,
|
|
3833
|
-
runtime
|
|
3834
|
-
});
|
|
3835
|
-
this.patch({
|
|
3836
|
-
busy: { deploying: false }
|
|
3837
|
-
});
|
|
3838
|
-
this.emitProgress({
|
|
3839
|
-
stage: "refreshing-instances",
|
|
3840
|
-
label: "Refreshing instances",
|
|
3841
|
-
progress: 96,
|
|
3842
|
-
status: "info",
|
|
3843
|
-
itemHash: result.itemHash,
|
|
3844
|
-
detail: "Reloading deployments and runtime state."
|
|
3845
|
-
});
|
|
3846
|
-
await this.refresh();
|
|
3918
|
+
throw new Error(
|
|
3919
|
+
attemptErrors.length > 0 ? `All compatible CRNs failed. ${attemptErrors.join(" | ")}` : lastError?.message ?? "All compatible CRNs failed."
|
|
3920
|
+
);
|
|
3847
3921
|
} catch (error) {
|
|
3848
3922
|
this.trace("deploy:error", error);
|
|
3849
3923
|
this.patch({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { onDestroy, onMount } from 'svelte'
|
|
3
|
+
import packageJson from '../../package.json'
|
|
3
4
|
|
|
4
5
|
import { createSponsorRelayController, formatDateTime, formatNumber, formatTierSpecLabel, joinMappedPorts, joinRequiredPortForwards, shortHash } from '../shared/index'
|
|
5
6
|
import AccordionSection from './components/AccordionSection.svelte'
|
|
@@ -37,6 +38,7 @@ export let apiHost = undefined
|
|
|
37
38
|
})
|
|
38
39
|
|
|
39
40
|
let state = controller.getState()
|
|
41
|
+
const versionLabel = `v${packageJson.version}`
|
|
40
42
|
|
|
41
43
|
onMount(async () => {
|
|
42
44
|
const unsubscribe = controller.subscribe((next) => {
|
|
@@ -62,7 +64,7 @@ export let apiHost = undefined
|
|
|
62
64
|
<aside class="panel">
|
|
63
65
|
<div class="panel-head">
|
|
64
66
|
<div>
|
|
65
|
-
<p class="eyebrow">Aleph VM credit deployer</p>
|
|
67
|
+
<p class="eyebrow">Aleph VM credit deployer <span class="eyebrow-version">{versionLabel}</span></p>
|
|
66
68
|
<h2>Sponsor Relay</h2>
|
|
67
69
|
</div>
|
|
68
70
|
<button class="refresh" type="button" on:click={() => controller.refresh()} disabled={state.busy.refreshing}>
|
|
@@ -322,6 +324,13 @@ export let apiHost = undefined
|
|
|
322
324
|
letter-spacing: 0.08em;
|
|
323
325
|
}
|
|
324
326
|
|
|
327
|
+
.eyebrow-version {
|
|
328
|
+
font-size: 0.62rem;
|
|
329
|
+
letter-spacing: 0.04em;
|
|
330
|
+
text-transform: none;
|
|
331
|
+
color: rgba(191, 219, 254, 0.82);
|
|
332
|
+
}
|
|
333
|
+
|
|
325
334
|
h2,
|
|
326
335
|
h3,
|
|
327
336
|
strong {
|