@le-space/ui 0.4.0 → 0.4.2
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.d.ts +0 -14
- package/react/index.js +478 -164
- package/shared/index.d.ts +2 -18
- package/shared/index.js +417 -161
- package/svelte/SponsorRelayFab.svelte +26 -9
package/shared/index.js
CHANGED
|
@@ -7,16 +7,6 @@ var ROOTFS_MISSING_STATE = {
|
|
|
7
7
|
label: "manifest missing",
|
|
8
8
|
detail: "Provide a manifest URL or paste manifest JSON."
|
|
9
9
|
};
|
|
10
|
-
var RELAY_PING_IDLE_STATE = {
|
|
11
|
-
tone: "idle",
|
|
12
|
-
sent: false,
|
|
13
|
-
received: false,
|
|
14
|
-
lastPeerId: null,
|
|
15
|
-
lastLatencyMs: null,
|
|
16
|
-
lastSentAt: null,
|
|
17
|
-
lastReceivedAt: null,
|
|
18
|
-
error: null
|
|
19
|
-
};
|
|
20
10
|
var IDLE_DEPLOYMENT_PROGRESS = {
|
|
21
11
|
stage: "idle",
|
|
22
12
|
label: "Ready",
|
|
@@ -28,7 +18,6 @@ var IDLE_DEPLOYMENT_PROGRESS = {
|
|
|
28
18
|
timestamp: Date.now()
|
|
29
19
|
};
|
|
30
20
|
var REFRESH_INTERVAL_MS = 3e4;
|
|
31
|
-
var RELAY_PING_INTERVAL_MS = 2e4;
|
|
32
21
|
var MANIFEST_SOURCE_REFRESH_DEBOUNCE_MS = 400;
|
|
33
22
|
var INSTANCE_RUNTIME_READY_REFRESH_MS = 3e4;
|
|
34
23
|
var INSTANCE_RUNTIME_PENDING_REFRESH_MS = 45e3;
|
|
@@ -38,7 +27,7 @@ var RECENT_INSTANCE_RUNTIME_GRACE_MS = 10 * 60 * 1e3;
|
|
|
38
27
|
var DEPLOYMENT_PENDING_WARNING_MS = 3 * 60 * 1e3;
|
|
39
28
|
|
|
40
29
|
// src/shared/package-version.ts
|
|
41
|
-
var UI_PACKAGE_VERSION = "0.4.
|
|
30
|
+
var UI_PACKAGE_VERSION = "0.4.2";
|
|
42
31
|
|
|
43
32
|
// src/shared/format.ts
|
|
44
33
|
function shortHash(value, head = 8, tail = 6) {
|
|
@@ -1247,16 +1236,6 @@ function normalizeBroadcastStatus(httpStatus, responseStatus) {
|
|
|
1247
1236
|
if (httpStatus === 202) return "pending";
|
|
1248
1237
|
return normalizeMessageStatus2(responseStatus);
|
|
1249
1238
|
}
|
|
1250
|
-
function isInvalidMessageFormatResponse2(response, payload) {
|
|
1251
|
-
if (response.status !== 422) return false;
|
|
1252
|
-
const details = payload?.details;
|
|
1253
|
-
if (typeof details === "string" && details.includes("InvalidMessageFormat")) return true;
|
|
1254
|
-
if (details && typeof details === "object") {
|
|
1255
|
-
const detailMessage = details.message;
|
|
1256
|
-
if (typeof detailMessage === "string" && detailMessage.includes("InvalidMessageFormat")) return true;
|
|
1257
|
-
}
|
|
1258
|
-
return false;
|
|
1259
|
-
}
|
|
1260
1239
|
function isRetryableBroadcastFailure(response, payload) {
|
|
1261
1240
|
if (response.status >= 500) return true;
|
|
1262
1241
|
const publicationStatus = payload?.publication_status?.status;
|
|
@@ -1278,25 +1257,22 @@ async function postBroadcastPayload2(body, options) {
|
|
|
1278
1257
|
};
|
|
1279
1258
|
}
|
|
1280
1259
|
async function broadcastAlephMessage2(message, options) {
|
|
1281
|
-
const
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
];
|
|
1286
|
-
for (let index = 0; index < attempts.length; index += 1) {
|
|
1287
|
-
const result = await postBroadcastPayload2(attempts[index], {
|
|
1260
|
+
const requestBody = { sync: options.sync ?? false, message };
|
|
1261
|
+
const maxAttempts = 3;
|
|
1262
|
+
for (let index = 0; index < maxAttempts; index += 1) {
|
|
1263
|
+
const result = await postBroadcastPayload2(requestBody, {
|
|
1288
1264
|
apiHost: options.apiHost,
|
|
1289
1265
|
fetch: options.fetch
|
|
1290
1266
|
});
|
|
1291
1267
|
if (result.httpStatus === 202 || normalizeBroadcastStatus(result.httpStatus, result.response?.message_status) !== "unknown" || result.httpStatus >= 200 && result.httpStatus < 300) {
|
|
1292
1268
|
return result;
|
|
1293
1269
|
}
|
|
1294
|
-
const canRetry = index <
|
|
1270
|
+
const canRetry = index < maxAttempts - 1 && isRetryableBroadcastFailure({ status: result.httpStatus }, result.response);
|
|
1295
1271
|
if (!canRetry) {
|
|
1296
1272
|
throw new Error(`Broadcast failed: ${result.httpStatus} ${JSON.stringify(result.response ?? {})}`);
|
|
1297
1273
|
}
|
|
1298
1274
|
}
|
|
1299
|
-
throw new Error("Broadcast failed:
|
|
1275
|
+
throw new Error("Broadcast failed: retry budget exhausted");
|
|
1300
1276
|
}
|
|
1301
1277
|
|
|
1302
1278
|
// ../core/src/forget.ts
|
|
@@ -1663,6 +1639,26 @@ function asString3(value) {
|
|
|
1663
1639
|
function asNumber3(value) {
|
|
1664
1640
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1665
1641
|
}
|
|
1642
|
+
function normalizeIpv6(value) {
|
|
1643
|
+
if (typeof value !== "string") return null;
|
|
1644
|
+
const normalized = value.trim().toLowerCase();
|
|
1645
|
+
if (!normalized) return null;
|
|
1646
|
+
const zoneIndex = normalized.indexOf("%");
|
|
1647
|
+
return zoneIndex >= 0 ? normalized.slice(0, zoneIndex) : normalized;
|
|
1648
|
+
}
|
|
1649
|
+
function isLikelyGlobalIpv6(value) {
|
|
1650
|
+
const normalized = normalizeIpv6(value);
|
|
1651
|
+
if (!normalized || normalized === "::" || normalized === "::1") return false;
|
|
1652
|
+
const firstHextetText = normalized.split(":", 1)[0];
|
|
1653
|
+
if (!firstHextetText) return false;
|
|
1654
|
+
const firstHextet = Number.parseInt(firstHextetText, 16);
|
|
1655
|
+
if (!Number.isFinite(firstHextet)) return false;
|
|
1656
|
+
if ((firstHextet & 65024) === 64512) return false;
|
|
1657
|
+
if ((firstHextet & 65472) === 65152) return false;
|
|
1658
|
+
if ((firstHextet & 65280) === 65280) return false;
|
|
1659
|
+
if ((firstHextet & 65472) === 65216) return false;
|
|
1660
|
+
return true;
|
|
1661
|
+
}
|
|
1666
1662
|
function findCrnByHash(crns, crnHash) {
|
|
1667
1663
|
return crns.find((crn) => crn.hash === crnHash) ?? null;
|
|
1668
1664
|
}
|
|
@@ -1743,10 +1739,22 @@ function describeRuntimeAvailability(runtime) {
|
|
|
1743
1739
|
const execution = runtime.execution ?? null;
|
|
1744
1740
|
const mappedPorts2 = runtime.mappedPorts ?? {};
|
|
1745
1741
|
const hostIpv4 = runtime.hostIpv4 ?? null;
|
|
1742
|
+
const ipv6 = runtime.ipv6 ?? null;
|
|
1746
1743
|
const proxyUrl = runtime.proxyUrl ?? null;
|
|
1747
1744
|
const schedulerSource = runtime.allocation?.source ?? null;
|
|
1748
1745
|
const webAccessActive = runtime.webAccess?.active ?? null;
|
|
1749
1746
|
const mappedPortCount = Object.keys(mappedPorts2).length;
|
|
1747
|
+
if (hostIpv4 && mappedPortCount > 0 && runtime.requirePublicGuestIpv6ForProxy === true && proxyUrl && !isLikelyGlobalIpv6(ipv6)) {
|
|
1748
|
+
return {
|
|
1749
|
+
state: "execution-invalid-public-ipv6",
|
|
1750
|
+
reason: `The CRN exposed runtime networking, but the guest IPv6${ipv6 ? ` (${ipv6})` : ""} is not globally routable. Proxy-backed HTTPS activation cannot succeed until the CRN reports a public guest IPv6.`,
|
|
1751
|
+
schedulerSource,
|
|
1752
|
+
executionSeen: Boolean(execution),
|
|
1753
|
+
webAccessActive,
|
|
1754
|
+
mappedPortCount,
|
|
1755
|
+
proxyUrl
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1750
1758
|
if (hostIpv4 && mappedPortCount > 0) {
|
|
1751
1759
|
return {
|
|
1752
1760
|
state: "ready",
|
|
@@ -1863,7 +1871,8 @@ async function fetchVmRuntime(args) {
|
|
|
1863
1871
|
hostIpv4,
|
|
1864
1872
|
ipv6,
|
|
1865
1873
|
proxyUrl,
|
|
1866
|
-
mappedPorts: mappedPorts2
|
|
1874
|
+
mappedPorts: mappedPorts2,
|
|
1875
|
+
requirePublicGuestIpv6ForProxy: args.requirePublicGuestIpv6ForProxy
|
|
1867
1876
|
});
|
|
1868
1877
|
return {
|
|
1869
1878
|
allocation,
|
|
@@ -1887,7 +1896,7 @@ async function waitForVmRuntime(args) {
|
|
|
1887
1896
|
let lastRuntime = await fetchVmRuntime(args);
|
|
1888
1897
|
args.onAttempt?.(lastRuntime, 1, attempts);
|
|
1889
1898
|
for (let attempt = 0; attempt < attempts - 1; attempt += 1) {
|
|
1890
|
-
if (lastRuntime.
|
|
1899
|
+
if (lastRuntime.diagnostics?.state === "ready" || lastRuntime.diagnostics?.state === "execution-invalid-public-ipv6") {
|
|
1891
1900
|
return lastRuntime;
|
|
1892
1901
|
}
|
|
1893
1902
|
await sleep2(delayMs);
|
|
@@ -2561,7 +2570,7 @@ async function waitForRelayBootstrapRegistration(args) {
|
|
|
2561
2570
|
return false;
|
|
2562
2571
|
});
|
|
2563
2572
|
if (match?.content) {
|
|
2564
|
-
|
|
2573
|
+
const visibleRegistration = {
|
|
2565
2574
|
itemHash: match.itemHash ?? null,
|
|
2566
2575
|
hash: match.hash ?? null,
|
|
2567
2576
|
sender: typeof match.address === "string" ? match.address : null,
|
|
@@ -2570,7 +2579,10 @@ async function waitForRelayBootstrapRegistration(args) {
|
|
|
2570
2579
|
multiaddrs: Array.isArray(match.content.multiaddrs) ? match.content.multiaddrs : [],
|
|
2571
2580
|
browserMultiaddrs: Array.isArray(match.content.browserMultiaddrs) ? match.content.browserMultiaddrs : []
|
|
2572
2581
|
};
|
|
2582
|
+
args.onAttempt?.(visibleRegistration, attempt + 1, attempts);
|
|
2583
|
+
return visibleRegistration;
|
|
2573
2584
|
}
|
|
2585
|
+
args.onAttempt?.(null, attempt + 1, attempts);
|
|
2574
2586
|
if (attempt < attempts - 1) {
|
|
2575
2587
|
await sleep(delayMs);
|
|
2576
2588
|
}
|
|
@@ -2683,6 +2695,16 @@ async function publishRelayBootstrapRegistration(args) {
|
|
|
2683
2695
|
|
|
2684
2696
|
// ../core/src/bootstrap-config.ts
|
|
2685
2697
|
var VM_BOOTSTRAP_CONFIG_AGGREGATE_KEY = "vm-bootstrap-config";
|
|
2698
|
+
var VM_BOOTSTRAP_CONFIG_SIGNAL_REF = "vm-bootstrap-config";
|
|
2699
|
+
var VM_BOOTSTRAP_CONFIG_SIGNAL_POST_TYPE = "vm-bootstrap-config-status";
|
|
2700
|
+
function parseJsonObject(value) {
|
|
2701
|
+
try {
|
|
2702
|
+
const parsed = JSON.parse(value);
|
|
2703
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
2704
|
+
} catch {
|
|
2705
|
+
return null;
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2686
2708
|
async function fetchVmBootstrapConfigAggregate(address, options) {
|
|
2687
2709
|
const requestUrl = new URL(`/api/v0/aggregates/${address}.json`, options.apiHost ?? DEFAULT_ALEPH_API_HOST2);
|
|
2688
2710
|
requestUrl.searchParams.set("keys", VM_BOOTSTRAP_CONFIG_AGGREGATE_KEY);
|
|
@@ -2802,6 +2824,89 @@ async function deleteVmBootstrapConfig(args) {
|
|
|
2802
2824
|
aggregate: content
|
|
2803
2825
|
};
|
|
2804
2826
|
}
|
|
2827
|
+
function normalizeVmBootstrapConfigSignalRecord(value) {
|
|
2828
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
2829
|
+
const candidate = value;
|
|
2830
|
+
const deploymentToken = typeof candidate.deploymentToken === "string" ? candidate.deploymentToken.trim() : "";
|
|
2831
|
+
const status = typeof candidate.status === "string" ? candidate.status.trim() : "";
|
|
2832
|
+
const profile = typeof candidate.profile === "string" ? candidate.profile.trim() : "";
|
|
2833
|
+
const ownerAddress = typeof candidate.ownerAddress === "string" ? candidate.ownerAddress.trim() : "";
|
|
2834
|
+
const instanceItemHash = typeof candidate.instanceItemHash === "string" ? candidate.instanceItemHash.trim() : "";
|
|
2835
|
+
const updatedAt = typeof candidate.updatedAt === "string" ? candidate.updatedAt.trim() : "";
|
|
2836
|
+
if (!deploymentToken || !status || !profile || !ownerAddress || !instanceItemHash || !updatedAt) {
|
|
2837
|
+
return null;
|
|
2838
|
+
}
|
|
2839
|
+
return {
|
|
2840
|
+
deploymentToken,
|
|
2841
|
+
status,
|
|
2842
|
+
profile,
|
|
2843
|
+
ownerAddress,
|
|
2844
|
+
instanceItemHash,
|
|
2845
|
+
updatedAt,
|
|
2846
|
+
publisherAddress: typeof candidate.publisherAddress === "string" && candidate.publisherAddress.trim() ? candidate.publisherAddress.trim() : null,
|
|
2847
|
+
authorization: candidate.authorization,
|
|
2848
|
+
peerId: typeof candidate.peerId === "string" && candidate.peerId.trim() ? candidate.peerId.trim() : null,
|
|
2849
|
+
probeMultiaddrs: Array.isArray(candidate.probeMultiaddrs) ? candidate.probeMultiaddrs.filter(
|
|
2850
|
+
(entry) => typeof entry === "string" && entry.trim().length > 0
|
|
2851
|
+
) : [],
|
|
2852
|
+
browserBootstrapMultiaddrs: Array.isArray(candidate.browserBootstrapMultiaddrs) ? candidate.browserBootstrapMultiaddrs.filter(
|
|
2853
|
+
(entry) => typeof entry === "string" && entry.trim().length > 0
|
|
2854
|
+
) : []
|
|
2855
|
+
};
|
|
2856
|
+
}
|
|
2857
|
+
async function fetchVmBootstrapConfigSignals(args) {
|
|
2858
|
+
const requestUrl = new URL("/api/v0/posts.json", args.apiHost ?? DEFAULT_ALEPH_API_HOST2);
|
|
2859
|
+
requestUrl.searchParams.set("refs", args.ref ?? VM_BOOTSTRAP_CONFIG_SIGNAL_REF);
|
|
2860
|
+
requestUrl.searchParams.set("types", args.postType ?? VM_BOOTSTRAP_CONFIG_SIGNAL_POST_TYPE);
|
|
2861
|
+
requestUrl.searchParams.set("pagination", String(Math.max(1, Number(args.pagination ?? 100))));
|
|
2862
|
+
requestUrl.searchParams.set("page", String(Math.max(1, Number(args.page ?? 1))));
|
|
2863
|
+
requestUrl.searchParams.set("sortOrder", "-1");
|
|
2864
|
+
const response = await args.fetch(requestUrl.toString(), { cache: "no-cache" });
|
|
2865
|
+
if (!response.ok) {
|
|
2866
|
+
throw new Error(`VM bootstrap config signal request failed: ${response.status}`);
|
|
2867
|
+
}
|
|
2868
|
+
const payload = await response.json();
|
|
2869
|
+
const expectedDeploymentToken = args.deploymentToken?.trim() ?? "";
|
|
2870
|
+
const expectedOwnerAddress = args.ownerAddress?.trim().toLowerCase() ?? "";
|
|
2871
|
+
const expectedInstanceItemHash = args.instanceItemHash?.trim() ?? "";
|
|
2872
|
+
return (payload.posts ?? []).map((entry) => {
|
|
2873
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
|
|
2874
|
+
const parsed = "content" in entry && entry.content && typeof entry.content === "object" && !Array.isArray(entry.content) ? entry.content : "item_content" in entry && typeof entry.item_content === "string" ? parseJsonObject(entry.item_content) : null;
|
|
2875
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
2876
|
+
const content = "deploymentToken" in parsed ? parsed : "content" in parsed && parsed.content && typeof parsed.content === "object" && !Array.isArray(parsed.content) ? parsed.content : null;
|
|
2877
|
+
if (!content || typeof content !== "object" || Array.isArray(content)) return null;
|
|
2878
|
+
return normalizeVmBootstrapConfigSignalRecord(content);
|
|
2879
|
+
}).filter((entry) => {
|
|
2880
|
+
if (!entry) return false;
|
|
2881
|
+
if (expectedDeploymentToken && entry.deploymentToken !== expectedDeploymentToken) return false;
|
|
2882
|
+
if (expectedOwnerAddress && entry.ownerAddress.toLowerCase() !== expectedOwnerAddress) return false;
|
|
2883
|
+
if (expectedInstanceItemHash && entry.instanceItemHash !== expectedInstanceItemHash) return false;
|
|
2884
|
+
return true;
|
|
2885
|
+
});
|
|
2886
|
+
}
|
|
2887
|
+
async function waitForVmBootstrapConfigSignal(args) {
|
|
2888
|
+
const attempts = Math.max(1, Number(args.attempts ?? 24));
|
|
2889
|
+
const delayMs = Math.max(0, Number(args.delayMs ?? 2500));
|
|
2890
|
+
const sleep2 = args.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
2891
|
+
const expectedStatus = args.expectedStatus ?? "applied";
|
|
2892
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
2893
|
+
const signals = await fetchVmBootstrapConfigSignals({
|
|
2894
|
+
deploymentToken: args.deploymentToken,
|
|
2895
|
+
ownerAddress: args.ownerAddress,
|
|
2896
|
+
instanceItemHash: args.instanceItemHash,
|
|
2897
|
+
fetch: args.fetch,
|
|
2898
|
+
apiHost: args.apiHost,
|
|
2899
|
+
ref: args.ref,
|
|
2900
|
+
postType: args.postType
|
|
2901
|
+
}).catch(() => []);
|
|
2902
|
+
const match = signals.find((entry) => entry.status === expectedStatus) ?? null;
|
|
2903
|
+
if (match) return match;
|
|
2904
|
+
if (attempt < attempts - 1) {
|
|
2905
|
+
await sleep2(delayMs);
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
return null;
|
|
2909
|
+
}
|
|
2805
2910
|
|
|
2806
2911
|
// ../core/src/bootstrap-reconcile.ts
|
|
2807
2912
|
function createRelayBootstrapRegistrationId(profile, instanceName, itemHash) {
|
|
@@ -2943,7 +3048,6 @@ function defaultState(props = {}) {
|
|
|
2943
3048
|
instances: [],
|
|
2944
3049
|
bootstrapRegistrations: [],
|
|
2945
3050
|
orphanBootstrapRegistrations: [],
|
|
2946
|
-
relayPing: RELAY_PING_IDLE_STATE,
|
|
2947
3051
|
lastDeploymentHash: null,
|
|
2948
3052
|
deploymentProgress: IDLE_DEPLOYMENT_PROGRESS
|
|
2949
3053
|
};
|
|
@@ -3170,57 +3274,6 @@ function compatibleCrnsForTier(crns, state) {
|
|
|
3170
3274
|
);
|
|
3171
3275
|
return filterDeployableCrns(crns, { spec });
|
|
3172
3276
|
}
|
|
3173
|
-
async function pingPeer(libp2p) {
|
|
3174
|
-
if (!libp2p || typeof libp2p !== "object") {
|
|
3175
|
-
return RELAY_PING_IDLE_STATE;
|
|
3176
|
-
}
|
|
3177
|
-
const candidate = libp2p;
|
|
3178
|
-
const peers = candidate.getPeers?.() ?? [];
|
|
3179
|
-
const firstPeer = peers[0];
|
|
3180
|
-
if (!firstPeer) {
|
|
3181
|
-
return {
|
|
3182
|
-
...RELAY_PING_IDLE_STATE,
|
|
3183
|
-
tone: "caution",
|
|
3184
|
-
error: "No connected relay peers available."
|
|
3185
|
-
};
|
|
3186
|
-
}
|
|
3187
|
-
const sentAt = Date.now();
|
|
3188
|
-
try {
|
|
3189
|
-
const pingFn = candidate.services?.ping?.ping?.bind(candidate.services.ping) ?? candidate.ping?.bind(candidate);
|
|
3190
|
-
if (!pingFn) {
|
|
3191
|
-
return {
|
|
3192
|
-
...RELAY_PING_IDLE_STATE,
|
|
3193
|
-
tone: "caution",
|
|
3194
|
-
sent: true,
|
|
3195
|
-
lastPeerId: String(firstPeer),
|
|
3196
|
-
lastSentAt: sentAt,
|
|
3197
|
-
error: "libp2p ping service not available."
|
|
3198
|
-
};
|
|
3199
|
-
}
|
|
3200
|
-
const latency = await pingFn(firstPeer);
|
|
3201
|
-
return {
|
|
3202
|
-
tone: "ok",
|
|
3203
|
-
sent: true,
|
|
3204
|
-
received: true,
|
|
3205
|
-
lastPeerId: String(firstPeer),
|
|
3206
|
-
lastLatencyMs: Number(latency),
|
|
3207
|
-
lastSentAt: sentAt,
|
|
3208
|
-
lastReceivedAt: Date.now(),
|
|
3209
|
-
error: null
|
|
3210
|
-
};
|
|
3211
|
-
} catch (error) {
|
|
3212
|
-
return {
|
|
3213
|
-
tone: "error",
|
|
3214
|
-
sent: true,
|
|
3215
|
-
received: false,
|
|
3216
|
-
lastPeerId: String(firstPeer),
|
|
3217
|
-
lastLatencyMs: null,
|
|
3218
|
-
lastSentAt: sentAt,
|
|
3219
|
-
lastReceivedAt: null,
|
|
3220
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3221
|
-
};
|
|
3222
|
-
}
|
|
3223
|
-
}
|
|
3224
3277
|
var UI_DEPLOY_WAIT_ATTEMPTS = 60;
|
|
3225
3278
|
var UI_DEPLOY_WAIT_DELAY_MS = 5e3;
|
|
3226
3279
|
var UI_RUNTIME_WAIT_ATTEMPTS = 40;
|
|
@@ -3251,7 +3304,6 @@ var SponsorRelayController = class {
|
|
|
3251
3304
|
subscribers = /* @__PURE__ */ new Set();
|
|
3252
3305
|
client;
|
|
3253
3306
|
refreshTimer = null;
|
|
3254
|
-
pingTimer = null;
|
|
3255
3307
|
manifestRefreshTimer = null;
|
|
3256
3308
|
stopWalletWatch = null;
|
|
3257
3309
|
props;
|
|
@@ -3307,8 +3359,7 @@ var SponsorRelayController = class {
|
|
|
3307
3359
|
...patch,
|
|
3308
3360
|
busy: patch.busy ? { ...this.state.busy, ...patch.busy } : this.state.busy,
|
|
3309
3361
|
wallet: patch.wallet ? { ...this.state.wallet, ...patch.wallet } : this.state.wallet,
|
|
3310
|
-
pricingSummary: patch.pricingSummary ? { ...this.state.pricingSummary, ...patch.pricingSummary } : this.state.pricingSummary
|
|
3311
|
-
relayPing: patch.relayPing ? { ...this.state.relayPing, ...patch.relayPing } : this.state.relayPing
|
|
3362
|
+
pricingSummary: patch.pricingSummary ? { ...this.state.pricingSummary, ...patch.pricingSummary } : this.state.pricingSummary
|
|
3312
3363
|
};
|
|
3313
3364
|
this.emit();
|
|
3314
3365
|
}
|
|
@@ -3341,11 +3392,13 @@ var SponsorRelayController = class {
|
|
|
3341
3392
|
if (!args.runtime.hostIpv4) {
|
|
3342
3393
|
throw new Error("Relay runtime did not expose a host IPv4 address.");
|
|
3343
3394
|
}
|
|
3344
|
-
|
|
3395
|
+
let runtime = args.runtime;
|
|
3396
|
+
let mappedPorts2 = runtime.mappedPorts ?? {};
|
|
3345
3397
|
const setupPort = mappedPorts2["80"]?.host ?? null;
|
|
3346
3398
|
if (!setupPort) {
|
|
3347
3399
|
throw new Error("Relay runtime did not expose the temporary setup endpoint.");
|
|
3348
3400
|
}
|
|
3401
|
+
let bootstrapSignalRelayMetadata = null;
|
|
3349
3402
|
if (profile === "orbitdb-relay-pinner") {
|
|
3350
3403
|
this.emitProgress({
|
|
3351
3404
|
stage: "deployment-confirmed",
|
|
@@ -3361,7 +3414,17 @@ var SponsorRelayController = class {
|
|
|
3361
3414
|
fetch: (url, init) => fetch(url, init),
|
|
3362
3415
|
attempts: 15,
|
|
3363
3416
|
delayMs: 4e3,
|
|
3364
|
-
httpTimeoutMs: 1e4
|
|
3417
|
+
httpTimeoutMs: 1e4,
|
|
3418
|
+
onAttempt: (result, attempt, attempts) => {
|
|
3419
|
+
this.emitProgress({
|
|
3420
|
+
stage: "deployment-confirmed",
|
|
3421
|
+
label: "Waiting for guest setup",
|
|
3422
|
+
progress: 93,
|
|
3423
|
+
status: result.ok ? "success" : "info",
|
|
3424
|
+
itemHash: args.itemHash,
|
|
3425
|
+
detail: result.ok ? `Setup endpoint ${attempt}/${attempts}: ready at http://${args.runtime.hostIpv4}:${setupPort}/health.` : `Setup endpoint ${attempt}/${attempts}: waiting for http://${args.runtime.hostIpv4}:${setupPort}/health.`
|
|
3426
|
+
});
|
|
3427
|
+
}
|
|
3365
3428
|
});
|
|
3366
3429
|
if (!setupHealth.ok) {
|
|
3367
3430
|
throw new Error(
|
|
@@ -3404,6 +3467,57 @@ var SponsorRelayController = class {
|
|
|
3404
3467
|
if (!args.deploymentToken) {
|
|
3405
3468
|
throw new Error("Missing deployment token for Aleph bootstrap config handoff.");
|
|
3406
3469
|
}
|
|
3470
|
+
if (runtime.proxyUrl && runtime.webAccess?.active !== true) {
|
|
3471
|
+
this.emitProgress({
|
|
3472
|
+
stage: "deployment-confirmed",
|
|
3473
|
+
label: "Waiting for HTTPS route",
|
|
3474
|
+
progress: 93,
|
|
3475
|
+
status: "warning",
|
|
3476
|
+
itemHash: args.itemHash,
|
|
3477
|
+
detail: "The relay received a 2n6 hostname, but it is not active yet. Waiting before handing HTTPS config to the guest."
|
|
3478
|
+
});
|
|
3479
|
+
let latestRuntime = runtime;
|
|
3480
|
+
for (let attempt = 0; attempt < UI_RUNTIME_WAIT_ATTEMPTS; attempt += 1) {
|
|
3481
|
+
if (latestRuntime.webAccess?.active === true) {
|
|
3482
|
+
break;
|
|
3483
|
+
}
|
|
3484
|
+
await new Promise(
|
|
3485
|
+
(resolve) => globalThis.setTimeout(resolve, UI_RUNTIME_WAIT_DELAY_MS)
|
|
3486
|
+
);
|
|
3487
|
+
latestRuntime = await fetchVmRuntime({
|
|
3488
|
+
itemHash: args.itemHash,
|
|
3489
|
+
fetch: (url, init) => fetch(url, init),
|
|
3490
|
+
crnHash: runtime.selectedCrn?.hash ?? runtime.allocation?.crnHash ?? void 0,
|
|
3491
|
+
crns: this.state.crns,
|
|
3492
|
+
crnListUrl: this.props.crnListUrl,
|
|
3493
|
+
requirePublicGuestIpv6ForProxy: true
|
|
3494
|
+
}).catch(() => latestRuntime);
|
|
3495
|
+
this.emitProgress({
|
|
3496
|
+
stage: "deployment-confirmed",
|
|
3497
|
+
label: "Waiting for HTTPS route",
|
|
3498
|
+
progress: 93,
|
|
3499
|
+
status: latestRuntime.webAccess?.active === true ? "info" : "warning",
|
|
3500
|
+
itemHash: args.itemHash,
|
|
3501
|
+
detail: `2n6 activation ${attempt + 1}/${UI_RUNTIME_WAIT_ATTEMPTS}: ${latestRuntime.webAccess?.active === true ? "active" : "still pending"} for ${latestRuntime.proxyUrl ?? runtime.proxyUrl ?? "reserved proxy URL"}.`
|
|
3502
|
+
});
|
|
3503
|
+
this.trace("deploy:bootstrap-proxy-activation", {
|
|
3504
|
+
itemHash: args.itemHash,
|
|
3505
|
+
attempt: attempt + 1,
|
|
3506
|
+
attempts: UI_RUNTIME_WAIT_ATTEMPTS,
|
|
3507
|
+
active: latestRuntime.webAccess?.active === true,
|
|
3508
|
+
proxyUrl: latestRuntime.proxyUrl ?? null
|
|
3509
|
+
});
|
|
3510
|
+
}
|
|
3511
|
+
runtime = latestRuntime;
|
|
3512
|
+
mappedPorts2 = runtime.mappedPorts ?? mappedPorts2;
|
|
3513
|
+
}
|
|
3514
|
+
if (!runtime.hostIpv4) {
|
|
3515
|
+
throw new Error(
|
|
3516
|
+
"Relay runtime lost its host IPv4 address while waiting for HTTPS activation."
|
|
3517
|
+
);
|
|
3518
|
+
}
|
|
3519
|
+
const runtimeHostIpv4 = runtime.hostIpv4;
|
|
3520
|
+
const activeProxyUrl = runtime.webAccess?.active === true ? runtime.proxyUrl ?? null : null;
|
|
3407
3521
|
this.emitProgress({
|
|
3408
3522
|
stage: "deployment-confirmed",
|
|
3409
3523
|
label: "Publishing relay config",
|
|
@@ -3421,9 +3535,9 @@ var SponsorRelayController = class {
|
|
|
3421
3535
|
expiresAt: new Date(Date.now() + 30 * 6e4).toISOString(),
|
|
3422
3536
|
status: "pending",
|
|
3423
3537
|
runtime: {
|
|
3424
|
-
publicIpv4:
|
|
3425
|
-
publicIpv6:
|
|
3426
|
-
proxyUrl:
|
|
3538
|
+
publicIpv4: runtimeHostIpv4,
|
|
3539
|
+
publicIpv6: runtime.ipv6 ?? null,
|
|
3540
|
+
proxyUrl: activeProxyUrl,
|
|
3427
3541
|
mappedPorts: mappedPorts2
|
|
3428
3542
|
},
|
|
3429
3543
|
bootstrap: {
|
|
@@ -3452,46 +3566,120 @@ var SponsorRelayController = class {
|
|
|
3452
3566
|
aggregateItemHash: bootstrapConfigPublication.aggregateItemHash,
|
|
3453
3567
|
aggregateStatus: bootstrapConfigPublication.aggregateStatus
|
|
3454
3568
|
});
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3569
|
+
this.emitProgress({
|
|
3570
|
+
stage: "deployment-confirmed",
|
|
3571
|
+
label: "Waiting for guest config",
|
|
3572
|
+
progress: 94,
|
|
3573
|
+
status: "info",
|
|
3574
|
+
itemHash: args.itemHash,
|
|
3575
|
+
detail: "Waiting for the relay VM to acknowledge that it consumed the Aleph bootstrap config."
|
|
3576
|
+
});
|
|
3577
|
+
const bootstrapConfigSignal = await waitForVmBootstrapConfigSignal({
|
|
3578
|
+
deploymentToken: args.deploymentToken,
|
|
3579
|
+
ownerAddress: this.state.wallet.address,
|
|
3580
|
+
instanceItemHash: args.itemHash,
|
|
3581
|
+
fetch: asJsonFetch,
|
|
3582
|
+
apiHost: this.client.apiHost,
|
|
3583
|
+
attempts: 40,
|
|
3584
|
+
delayMs: 3e3
|
|
3585
|
+
});
|
|
3586
|
+
this.trace("deploy:bootstrap-config-signal-visible", {
|
|
3587
|
+
itemHash: args.itemHash,
|
|
3588
|
+
deploymentToken: args.deploymentToken,
|
|
3589
|
+
bootstrapConfigSignal
|
|
3590
|
+
});
|
|
3591
|
+
if (!bootstrapConfigSignal) {
|
|
3592
|
+
throw new Error(
|
|
3593
|
+
"The relay VM did not confirm that it applied the Aleph bootstrap config in time."
|
|
3594
|
+
);
|
|
3595
|
+
}
|
|
3596
|
+
bootstrapSignalRelayMetadata = typeof bootstrapConfigSignal.peerId === "string" && bootstrapConfigSignal.peerId.length > 0 && Array.isArray(bootstrapConfigSignal.probeMultiaddrs) && bootstrapConfigSignal.probeMultiaddrs.length > 0 ? {
|
|
3597
|
+
peerId: bootstrapConfigSignal.peerId,
|
|
3598
|
+
probeMultiaddrs: bootstrapConfigSignal.probeMultiaddrs,
|
|
3599
|
+
browserBootstrapMultiaddrs: bootstrapConfigSignal.browserBootstrapMultiaddrs ?? []
|
|
3600
|
+
} : null;
|
|
3601
|
+
if (runtime.proxyUrl && runtime.webAccess?.active !== true) {
|
|
3602
|
+
this.trace("deploy:bootstrap-proxy-fallback", {
|
|
3603
|
+
itemHash: args.itemHash,
|
|
3604
|
+
deploymentToken: args.deploymentToken,
|
|
3605
|
+
proxyUrl: runtime.proxyUrl,
|
|
3606
|
+
bootstrapSignalRelayMetadata
|
|
3607
|
+
});
|
|
3608
|
+
this.emitProgress({
|
|
3609
|
+
stage: "deployment-confirmed",
|
|
3610
|
+
label: "Continuing without HTTPS route",
|
|
3611
|
+
progress: 94,
|
|
3612
|
+
status: "warning",
|
|
3475
3613
|
itemHash: args.itemHash,
|
|
3476
|
-
|
|
3477
|
-
attempts: result.attempts,
|
|
3478
|
-
requestUrl: result.requestUrl,
|
|
3479
|
-
metadataUrl: result.metadataUrl,
|
|
3480
|
-
hostIpv4: result.hostIpv4,
|
|
3481
|
-
setupPort: result.setupPort,
|
|
3482
|
-
ok: result.ok,
|
|
3483
|
-
status: result.status,
|
|
3484
|
-
ready: result.ready,
|
|
3485
|
-
error: result.error ?? null,
|
|
3486
|
-
payloadStatus: payload && typeof payload.status === "string" ? payload.status : null,
|
|
3487
|
-
peerId: metadata && typeof metadata.peer_id === "string" ? metadata.peer_id : null,
|
|
3488
|
-
probeMultiaddrCount: Array.isArray(metadata?.probe_multiaddrs) ? metadata.probe_multiaddrs.length : 0,
|
|
3489
|
-
browserBootstrapMultiaddrCount: Array.isArray(
|
|
3490
|
-
metadata?.browser_bootstrap_multiaddrs
|
|
3491
|
-
) ? metadata.browser_bootstrap_multiaddrs.length : 0
|
|
3614
|
+
detail: "The 2n6 HTTPS route stayed inactive, so guest setup continued without Caddy. Secure browser routing can be enabled later when the proxy becomes available."
|
|
3492
3615
|
});
|
|
3616
|
+
runtime = {
|
|
3617
|
+
...runtime,
|
|
3618
|
+
proxyUrl: null,
|
|
3619
|
+
webAccess: runtime.webAccess ? {
|
|
3620
|
+
...runtime.webAccess,
|
|
3621
|
+
active: false
|
|
3622
|
+
} : runtime.webAccess
|
|
3623
|
+
};
|
|
3493
3624
|
}
|
|
3494
|
-
}
|
|
3625
|
+
}
|
|
3626
|
+
let relayMetadata = bootstrapSignalRelayMetadata;
|
|
3627
|
+
if (relayMetadata) {
|
|
3628
|
+
this.trace("deploy:relay-metadata-from-bootstrap-signal", {
|
|
3629
|
+
itemHash: args.itemHash,
|
|
3630
|
+
peerId: relayMetadata.peerId,
|
|
3631
|
+
probeMultiaddrCount: relayMetadata.probeMultiaddrs.length,
|
|
3632
|
+
browserBootstrapMultiaddrCount: relayMetadata.browserBootstrapMultiaddrs.length
|
|
3633
|
+
});
|
|
3634
|
+
} else {
|
|
3635
|
+
this.emitProgress({
|
|
3636
|
+
stage: "publishing-bootstrap",
|
|
3637
|
+
label: "Waiting for secure relay metadata",
|
|
3638
|
+
progress: 95,
|
|
3639
|
+
status: "warning",
|
|
3640
|
+
itemHash: args.itemHash,
|
|
3641
|
+
detail: "Runtime networking is available. Waiting for the secure relay metadata endpoint to report final public multiaddrs."
|
|
3642
|
+
});
|
|
3643
|
+
relayMetadata = await fetchRelayMetadataForRuntime({
|
|
3644
|
+
runtime,
|
|
3645
|
+
fetch: (url, init) => fetch(url, init),
|
|
3646
|
+
preferSecureMetadata: profile === "uc-go-peer" && Boolean(runtime.proxyUrl),
|
|
3647
|
+
attempts: 80,
|
|
3648
|
+
delayMs: 3e3,
|
|
3649
|
+
timeoutMs: 24e4,
|
|
3650
|
+
onAttempt: (result) => {
|
|
3651
|
+
const payload = result.payload && typeof result.payload === "object" ? result.payload : null;
|
|
3652
|
+
const metadata = payload?.metadata && typeof payload.metadata === "object" ? payload.metadata : null;
|
|
3653
|
+
this.trace("deploy:relay-metadata-attempt", {
|
|
3654
|
+
itemHash: args.itemHash,
|
|
3655
|
+
attempt: result.attempt,
|
|
3656
|
+
attempts: result.attempts,
|
|
3657
|
+
requestUrl: result.requestUrl,
|
|
3658
|
+
metadataUrl: result.metadataUrl,
|
|
3659
|
+
hostIpv4: result.hostIpv4,
|
|
3660
|
+
setupPort: result.setupPort,
|
|
3661
|
+
ok: result.ok,
|
|
3662
|
+
status: result.status,
|
|
3663
|
+
ready: result.ready,
|
|
3664
|
+
error: result.error ?? null,
|
|
3665
|
+
payloadStatus: payload && typeof payload.status === "string" ? payload.status : null,
|
|
3666
|
+
peerId: metadata && typeof metadata.peer_id === "string" ? metadata.peer_id : null,
|
|
3667
|
+
probeMultiaddrCount: Array.isArray(metadata?.probe_multiaddrs) ? metadata.probe_multiaddrs.length : 0,
|
|
3668
|
+
browserBootstrapMultiaddrCount: Array.isArray(
|
|
3669
|
+
metadata?.browser_bootstrap_multiaddrs
|
|
3670
|
+
) ? metadata.browser_bootstrap_multiaddrs.length : 0
|
|
3671
|
+
});
|
|
3672
|
+
this.emitProgress({
|
|
3673
|
+
stage: "publishing-bootstrap",
|
|
3674
|
+
label: result.ready ? "Secure relay metadata ready" : "Waiting for secure relay metadata",
|
|
3675
|
+
progress: 95,
|
|
3676
|
+
status: result.ready ? "success" : "warning",
|
|
3677
|
+
itemHash: args.itemHash,
|
|
3678
|
+
detail: result.ready ? `Relay metadata ${result.attempt}/${result.attempts}: secure endpoint responded and public multiaddrs are available.` : `Relay metadata ${result.attempt}/${result.attempts}: waiting for ${result.metadataUrl}.`
|
|
3679
|
+
});
|
|
3680
|
+
}
|
|
3681
|
+
});
|
|
3682
|
+
}
|
|
3495
3683
|
if (!relayMetadata) {
|
|
3496
3684
|
throw new Error("Relay metadata did not include a peer ID and public multiaddrs.");
|
|
3497
3685
|
}
|
|
@@ -3515,7 +3703,17 @@ var SponsorRelayController = class {
|
|
|
3515
3703
|
fetch: asJsonFetch,
|
|
3516
3704
|
apiHost: this.client.apiHost,
|
|
3517
3705
|
attempts: 24,
|
|
3518
|
-
delayMs: 2500
|
|
3706
|
+
delayMs: 2500,
|
|
3707
|
+
onAttempt: (match, attempt, attempts) => {
|
|
3708
|
+
this.emitProgress({
|
|
3709
|
+
stage: "publishing-bootstrap",
|
|
3710
|
+
label: "Waiting for guest bootstrap registration",
|
|
3711
|
+
progress: 97,
|
|
3712
|
+
status: match ? "success" : "info",
|
|
3713
|
+
itemHash: args.itemHash,
|
|
3714
|
+
detail: match ? `Guest bootstrap registration ${attempt}/${attempts}: visible on Aleph.` : `Guest bootstrap registration ${attempt}/${attempts}: still waiting for the relay VM registration on Aleph.`
|
|
3715
|
+
});
|
|
3716
|
+
}
|
|
3519
3717
|
});
|
|
3520
3718
|
this.trace("deploy:bootstrap-registration-visible", {
|
|
3521
3719
|
itemHash: args.itemHash,
|
|
@@ -3566,7 +3764,17 @@ var SponsorRelayController = class {
|
|
|
3566
3764
|
fetch: asJsonFetch,
|
|
3567
3765
|
apiHost: this.client.apiHost,
|
|
3568
3766
|
attempts: 24,
|
|
3569
|
-
delayMs: 2500
|
|
3767
|
+
delayMs: 2500,
|
|
3768
|
+
onAttempt: (match, attempt, attempts) => {
|
|
3769
|
+
this.emitProgress({
|
|
3770
|
+
stage: "publishing-bootstrap",
|
|
3771
|
+
label: "Waiting for bootstrap fallback",
|
|
3772
|
+
progress: 98,
|
|
3773
|
+
status: match ? "success" : "warning",
|
|
3774
|
+
itemHash: args.itemHash,
|
|
3775
|
+
detail: match ? `Bootstrap fallback ${attempt}/${attempts}: visible on Aleph.` : `Bootstrap fallback ${attempt}/${attempts}: waiting for the browser-published registration on Aleph.`
|
|
3776
|
+
});
|
|
3777
|
+
}
|
|
3570
3778
|
});
|
|
3571
3779
|
this.trace("deploy:bootstrap-registration-fallback-visible", {
|
|
3572
3780
|
itemHash: args.itemHash,
|
|
@@ -3612,6 +3820,15 @@ var SponsorRelayController = class {
|
|
|
3612
3820
|
const currentProgress = this.state.deploymentProgress;
|
|
3613
3821
|
const currentProgressIsForLatestHash = currentProgress.itemHash === itemHash || currentProgress.itemHash == null && currentProgress.stage !== "idle" && currentProgress.stage !== "error";
|
|
3614
3822
|
const keepTerminalSuccess = this.lastCompletedDeploymentHash === itemHash || currentProgressIsForLatestHash && currentProgress.stage === "completed" && currentProgress.status === "success";
|
|
3823
|
+
if (currentProgressIsForLatestHash && currentProgress.stage === "publishing-bootstrap") {
|
|
3824
|
+
this.trace("progress:retain-active-phase", {
|
|
3825
|
+
reason: "latest refresh would overwrite active relay metadata/bootstrap progress",
|
|
3826
|
+
itemHash,
|
|
3827
|
+
label: currentProgress.label,
|
|
3828
|
+
progress: currentProgress.progress
|
|
3829
|
+
});
|
|
3830
|
+
return;
|
|
3831
|
+
}
|
|
3615
3832
|
const status = latest.details.messageStatus;
|
|
3616
3833
|
if (status === "rejected") {
|
|
3617
3834
|
this.emitProgress({
|
|
@@ -3742,16 +3959,11 @@ var SponsorRelayController = class {
|
|
|
3742
3959
|
this.refreshTimer = setInterval(() => {
|
|
3743
3960
|
void this.refresh();
|
|
3744
3961
|
}, REFRESH_INTERVAL_MS);
|
|
3745
|
-
this.pingTimer = setInterval(() => {
|
|
3746
|
-
void this.refreshRelayPing();
|
|
3747
|
-
}, RELAY_PING_INTERVAL_MS);
|
|
3748
|
-
await this.refreshRelayPing();
|
|
3749
3962
|
this.patch({ ready: true });
|
|
3750
3963
|
this.trace("init:ready");
|
|
3751
3964
|
}
|
|
3752
3965
|
destroy() {
|
|
3753
3966
|
if (this.refreshTimer) clearInterval(this.refreshTimer);
|
|
3754
|
-
if (this.pingTimer) clearInterval(this.pingTimer);
|
|
3755
3967
|
if (this.manifestRefreshTimer) clearTimeout(this.manifestRefreshTimer);
|
|
3756
3968
|
this.stopWalletWatch?.();
|
|
3757
3969
|
}
|
|
@@ -4046,11 +4258,6 @@ var SponsorRelayController = class {
|
|
|
4046
4258
|
});
|
|
4047
4259
|
}
|
|
4048
4260
|
}
|
|
4049
|
-
async refreshRelayPing() {
|
|
4050
|
-
const relayPing = await pingPeer(this.props.libp2p);
|
|
4051
|
-
this.patch({ relayPing });
|
|
4052
|
-
this.trace("libp2p:relay-ping", relayPing);
|
|
4053
|
-
}
|
|
4054
4261
|
async deploy() {
|
|
4055
4262
|
this.trace("deploy:start", {
|
|
4056
4263
|
wallet: this.state.wallet.address,
|
|
@@ -4167,7 +4374,7 @@ var SponsorRelayController = class {
|
|
|
4167
4374
|
fetch: (url, init) => fetch(url, init),
|
|
4168
4375
|
attempts: UI_DEPLOY_WAIT_ATTEMPTS,
|
|
4169
4376
|
delayMs: UI_DEPLOY_WAIT_DELAY_MS,
|
|
4170
|
-
onAttempt: (inspectionResult) => {
|
|
4377
|
+
onAttempt: (inspectionResult, attempt, attempts) => {
|
|
4171
4378
|
if (inspectionResult.status === "processed") {
|
|
4172
4379
|
this.emitProgress({
|
|
4173
4380
|
stage: "deployment-confirmed",
|
|
@@ -4175,7 +4382,7 @@ var SponsorRelayController = class {
|
|
|
4175
4382
|
progress: 82,
|
|
4176
4383
|
status: "success",
|
|
4177
4384
|
itemHash: result.itemHash,
|
|
4178
|
-
detail:
|
|
4385
|
+
detail: `Aleph processing ${attempt}/${attempts}: instance message processed.`
|
|
4179
4386
|
});
|
|
4180
4387
|
return;
|
|
4181
4388
|
}
|
|
@@ -4197,7 +4404,7 @@ var SponsorRelayController = class {
|
|
|
4197
4404
|
progress: 76,
|
|
4198
4405
|
status: "info",
|
|
4199
4406
|
itemHash: result.itemHash,
|
|
4200
|
-
detail:
|
|
4407
|
+
detail: `Aleph processing ${attempt}/${attempts}: waiting for the instance message to be processed.`
|
|
4201
4408
|
});
|
|
4202
4409
|
}
|
|
4203
4410
|
});
|
|
@@ -4261,17 +4468,29 @@ var SponsorRelayController = class {
|
|
|
4261
4468
|
crnHash: candidateCrn.hash,
|
|
4262
4469
|
crns: this.state.crns,
|
|
4263
4470
|
crnListUrl: this.props.crnListUrl,
|
|
4471
|
+
requirePublicGuestIpv6ForProxy: true,
|
|
4264
4472
|
attempts: UI_RUNTIME_WAIT_ATTEMPTS,
|
|
4265
4473
|
delayMs: UI_RUNTIME_WAIT_DELAY_MS,
|
|
4266
|
-
onAttempt: (runtime2) => {
|
|
4267
|
-
if (runtime2.
|
|
4474
|
+
onAttempt: (runtime2, attempt, attempts) => {
|
|
4475
|
+
if (runtime2.diagnostics?.state === "ready") {
|
|
4268
4476
|
this.emitProgress({
|
|
4269
4477
|
stage: "deployment-confirmed",
|
|
4270
4478
|
label: "Runtime ready",
|
|
4271
4479
|
progress: 92,
|
|
4272
4480
|
status: "info",
|
|
4273
4481
|
itemHash: result.itemHash,
|
|
4274
|
-
detail:
|
|
4482
|
+
detail: `Runtime ${attempt}/${attempts}: networking and mapped ports are now available.`
|
|
4483
|
+
});
|
|
4484
|
+
return;
|
|
4485
|
+
}
|
|
4486
|
+
if (runtime2.diagnostics?.state === "execution-invalid-public-ipv6") {
|
|
4487
|
+
this.emitProgress({
|
|
4488
|
+
stage: "deployment-confirmed",
|
|
4489
|
+
label: "Rejecting unusable runtime",
|
|
4490
|
+
progress: 91,
|
|
4491
|
+
status: "warning",
|
|
4492
|
+
itemHash: result.itemHash,
|
|
4493
|
+
detail: runtime2.diagnostics?.reason ? `Runtime ${attempt}/${attempts}: ${runtime2.diagnostics.reason} Cleaning up this CRN attempt before retrying another relay host.` : `Runtime ${attempt}/${attempts}: the CRN exposed unusable guest networking. Cleaning up this CRN attempt before retrying another relay host.`
|
|
4275
4494
|
});
|
|
4276
4495
|
return;
|
|
4277
4496
|
}
|
|
@@ -4281,11 +4500,11 @@ var SponsorRelayController = class {
|
|
|
4281
4500
|
progress: 90,
|
|
4282
4501
|
status: "warning",
|
|
4283
4502
|
itemHash: result.itemHash,
|
|
4284
|
-
detail: runtime2.diagnostics?.reason
|
|
4503
|
+
detail: runtime2.diagnostics?.reason ? `Runtime ${attempt}/${attempts}: ${runtime2.diagnostics.reason}` : `Runtime ${attempt}/${attempts}: waiting for CRN runtime networking and mapped ports.`
|
|
4285
4504
|
});
|
|
4286
4505
|
}
|
|
4287
4506
|
});
|
|
4288
|
-
if (
|
|
4507
|
+
if (runtime.diagnostics?.state !== "ready") {
|
|
4289
4508
|
throw new Error(
|
|
4290
4509
|
runtime.diagnostics?.reason ?? "Deployment was processed, but runtime networking never exposed mapped ports."
|
|
4291
4510
|
);
|
|
@@ -4334,6 +4553,7 @@ var SponsorRelayController = class {
|
|
|
4334
4553
|
} catch (error) {
|
|
4335
4554
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
4336
4555
|
const attemptLabel = crnDisplayName;
|
|
4556
|
+
const failedDueToInvalidIpv6 = /not globally routable|public guest IPv6/i.test(lastError.message);
|
|
4337
4557
|
attemptErrors.push(`${attemptLabel}: ${lastError.message}`);
|
|
4338
4558
|
this.trace("deploy:crn-attempt-error", {
|
|
4339
4559
|
crnHash: candidateCrn.hash,
|
|
@@ -4342,7 +4562,23 @@ var SponsorRelayController = class {
|
|
|
4342
4562
|
error: lastError.message
|
|
4343
4563
|
});
|
|
4344
4564
|
if (attemptItemHash) {
|
|
4565
|
+
this.emitProgress({
|
|
4566
|
+
stage: "deployment-confirmed",
|
|
4567
|
+
label: failedDueToInvalidIpv6 ? "Rejecting unusable runtime" : "Cleaning up failed attempt",
|
|
4568
|
+
progress: failedDueToInvalidIpv6 ? 91 : 16,
|
|
4569
|
+
status: "warning",
|
|
4570
|
+
itemHash: attemptItemHash,
|
|
4571
|
+
detail: failedDueToInvalidIpv6 ? `${attemptLabel} exposed runtime networking that cannot support proxy-backed HTTPS. Cleaning up this attempt before retrying another CRN.` : `${attemptLabel} failed: ${lastError.message} Cleaning up this attempt before retrying another CRN.`
|
|
4572
|
+
});
|
|
4345
4573
|
if (attemptDeploymentToken) {
|
|
4574
|
+
this.emitProgress({
|
|
4575
|
+
stage: "deployment-confirmed",
|
|
4576
|
+
label: "Cleaning up failed attempt",
|
|
4577
|
+
progress: 17,
|
|
4578
|
+
status: "warning",
|
|
4579
|
+
itemHash: attemptItemHash,
|
|
4580
|
+
detail: "Removing bootstrap configuration for the failed attempt. MetaMask may request a cleanup signature."
|
|
4581
|
+
});
|
|
4346
4582
|
this.trace("deploy:bootstrap-config-cleanup-start", {
|
|
4347
4583
|
itemHash: attemptItemHash,
|
|
4348
4584
|
deploymentToken: attemptDeploymentToken
|
|
@@ -4370,6 +4606,14 @@ var SponsorRelayController = class {
|
|
|
4370
4606
|
});
|
|
4371
4607
|
});
|
|
4372
4608
|
}
|
|
4609
|
+
this.emitProgress({
|
|
4610
|
+
stage: "deployment-confirmed",
|
|
4611
|
+
label: "Cleaning up failed attempt",
|
|
4612
|
+
progress: 18,
|
|
4613
|
+
status: "warning",
|
|
4614
|
+
itemHash: attemptItemHash,
|
|
4615
|
+
detail: "Forgetting the failed deployment attempt on Aleph. MetaMask may request another cleanup signature."
|
|
4616
|
+
});
|
|
4373
4617
|
this.trace("deploy:forget-failed-attempt-start", {
|
|
4374
4618
|
itemHash: attemptItemHash,
|
|
4375
4619
|
crnHash: candidateCrn.hash,
|
|
@@ -4402,11 +4646,11 @@ var SponsorRelayController = class {
|
|
|
4402
4646
|
if (candidateIndex < orderedCrns.length - 1) {
|
|
4403
4647
|
this.emitProgress({
|
|
4404
4648
|
stage: "selecting-crn",
|
|
4405
|
-
label:
|
|
4649
|
+
label: "Retrying on next CRN",
|
|
4406
4650
|
progress: 20,
|
|
4407
4651
|
status: "warning",
|
|
4408
4652
|
itemHash: attemptItemHash,
|
|
4409
|
-
detail: `${attemptLabel}
|
|
4653
|
+
detail: `${attemptLabel} was discarded: ${lastError.message}`,
|
|
4410
4654
|
error: lastError.message
|
|
4411
4655
|
});
|
|
4412
4656
|
continue;
|
|
@@ -4438,6 +4682,17 @@ var SponsorRelayController = class {
|
|
|
4438
4682
|
this.patch({ errorText: "Connect MetaMask before deleting instances." });
|
|
4439
4683
|
return;
|
|
4440
4684
|
}
|
|
4685
|
+
const linkedRegistrationHashes = Array.from(
|
|
4686
|
+
new Set(
|
|
4687
|
+
this.state.bootstrapRegistrations.filter((entry) => entry.instanceItemHash === instanceHash).map(
|
|
4688
|
+
(entry) => entry.messageHash?.trim() ?? entry.itemHash?.trim() ?? entry.hash?.trim() ?? null
|
|
4689
|
+
).filter((value) => Boolean(value))
|
|
4690
|
+
)
|
|
4691
|
+
);
|
|
4692
|
+
const hashesToForget = Array.from(
|
|
4693
|
+
/* @__PURE__ */ new Set([instanceHash, ...linkedRegistrationHashes])
|
|
4694
|
+
);
|
|
4695
|
+
const linkedRegistrationCount = linkedRegistrationHashes.length;
|
|
4441
4696
|
this.patch({
|
|
4442
4697
|
busy: { deletingInstanceHash: instanceHash },
|
|
4443
4698
|
errorText: null,
|
|
@@ -4450,12 +4705,12 @@ var SponsorRelayController = class {
|
|
|
4450
4705
|
progress: 6,
|
|
4451
4706
|
status: "warning",
|
|
4452
4707
|
itemHash: instanceHash,
|
|
4453
|
-
detail: "Preparing Aleph FORGET message."
|
|
4708
|
+
detail: linkedRegistrationCount > 0 ? `Preparing Aleph FORGET message for the instance and ${linkedRegistrationCount} linked bootstrap registration${linkedRegistrationCount === 1 ? "" : "s"}.` : "Preparing Aleph FORGET message."
|
|
4454
4709
|
});
|
|
4455
4710
|
await forgetAlephMessages({
|
|
4456
4711
|
sender: this.state.wallet.address,
|
|
4457
|
-
hashes:
|
|
4458
|
-
reason: "Deleted from Sponsor Relay panel",
|
|
4712
|
+
hashes: hashesToForget,
|
|
4713
|
+
reason: linkedRegistrationCount > 0 ? `Deleted instance and ${linkedRegistrationCount} linked relay bootstrap registration${linkedRegistrationCount === 1 ? "" : "s"} from Sponsor Relay panel` : "Deleted from Sponsor Relay panel",
|
|
4459
4714
|
signer: personalSign,
|
|
4460
4715
|
hasher: sha256Hex,
|
|
4461
4716
|
fetch: asJsonFetch,
|
|
@@ -4474,17 +4729,20 @@ var SponsorRelayController = class {
|
|
|
4474
4729
|
progress: 92,
|
|
4475
4730
|
status: "info",
|
|
4476
4731
|
itemHash: instanceHash,
|
|
4477
|
-
detail: "Reloading current deployments."
|
|
4732
|
+
detail: linkedRegistrationCount > 0 ? "Reloading current deployments and relay bootstrap registrations." : "Reloading current deployments."
|
|
4478
4733
|
});
|
|
4479
4734
|
await this.refresh();
|
|
4480
|
-
this.trace("delete:submitted", {
|
|
4735
|
+
this.trace("delete:submitted", {
|
|
4736
|
+
instanceHash,
|
|
4737
|
+
linkedRegistrationHashes
|
|
4738
|
+
});
|
|
4481
4739
|
this.emitProgress({
|
|
4482
4740
|
stage: "completed",
|
|
4483
4741
|
label: "Delete completed",
|
|
4484
4742
|
progress: 100,
|
|
4485
4743
|
status: "success",
|
|
4486
4744
|
itemHash: instanceHash,
|
|
4487
|
-
detail: "Delete request submitted and deployments refreshed.",
|
|
4745
|
+
detail: linkedRegistrationCount > 0 ? `Delete request submitted, including ${linkedRegistrationCount} linked bootstrap registration${linkedRegistrationCount === 1 ? "" : "s"}, and state refreshed.` : "Delete request submitted and deployments refreshed.",
|
|
4488
4746
|
error: null
|
|
4489
4747
|
});
|
|
4490
4748
|
} catch (error) {
|
|
@@ -4578,8 +4836,6 @@ export {
|
|
|
4578
4836
|
MANIFEST_SOURCE_REFRESH_DEBOUNCE_MS,
|
|
4579
4837
|
RECENT_INSTANCE_RUNTIME_GRACE_MS,
|
|
4580
4838
|
REFRESH_INTERVAL_MS,
|
|
4581
|
-
RELAY_PING_IDLE_STATE,
|
|
4582
|
-
RELAY_PING_INTERVAL_MS,
|
|
4583
4839
|
ROOTFS_MISSING_STATE,
|
|
4584
4840
|
STALE_INSTANCE_ALLOCATION_COOLDOWN_MS,
|
|
4585
4841
|
SponsorRelayController,
|