@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/react/index.js
CHANGED
|
@@ -10,16 +10,6 @@ var ROOTFS_MISSING_STATE = {
|
|
|
10
10
|
label: "manifest missing",
|
|
11
11
|
detail: "Provide a manifest URL or paste manifest JSON."
|
|
12
12
|
};
|
|
13
|
-
var RELAY_PING_IDLE_STATE = {
|
|
14
|
-
tone: "idle",
|
|
15
|
-
sent: false,
|
|
16
|
-
received: false,
|
|
17
|
-
lastPeerId: null,
|
|
18
|
-
lastLatencyMs: null,
|
|
19
|
-
lastSentAt: null,
|
|
20
|
-
lastReceivedAt: null,
|
|
21
|
-
error: null
|
|
22
|
-
};
|
|
23
13
|
var IDLE_DEPLOYMENT_PROGRESS = {
|
|
24
14
|
stage: "idle",
|
|
25
15
|
label: "Ready",
|
|
@@ -31,7 +21,6 @@ var IDLE_DEPLOYMENT_PROGRESS = {
|
|
|
31
21
|
timestamp: Date.now()
|
|
32
22
|
};
|
|
33
23
|
var REFRESH_INTERVAL_MS = 3e4;
|
|
34
|
-
var RELAY_PING_INTERVAL_MS = 2e4;
|
|
35
24
|
var MANIFEST_SOURCE_REFRESH_DEBOUNCE_MS = 400;
|
|
36
25
|
var INSTANCE_RUNTIME_STALE_REFRESH_MS = 5 * 6e4;
|
|
37
26
|
var STALE_INSTANCE_ALLOCATION_COOLDOWN_MS = 5 * 60 * 1e3;
|
|
@@ -39,7 +28,7 @@ var RECENT_INSTANCE_RUNTIME_GRACE_MS = 10 * 60 * 1e3;
|
|
|
39
28
|
var DEPLOYMENT_PENDING_WARNING_MS = 3 * 60 * 1e3;
|
|
40
29
|
|
|
41
30
|
// src/shared/package-version.ts
|
|
42
|
-
var UI_PACKAGE_VERSION = "0.4.
|
|
31
|
+
var UI_PACKAGE_VERSION = "0.4.2";
|
|
43
32
|
|
|
44
33
|
// src/shared/format.ts
|
|
45
34
|
function shortHash(value, head = 8, tail = 6) {
|
|
@@ -1248,16 +1237,6 @@ function normalizeBroadcastStatus(httpStatus, responseStatus) {
|
|
|
1248
1237
|
if (httpStatus === 202) return "pending";
|
|
1249
1238
|
return normalizeMessageStatus2(responseStatus);
|
|
1250
1239
|
}
|
|
1251
|
-
function isInvalidMessageFormatResponse2(response, payload) {
|
|
1252
|
-
if (response.status !== 422) return false;
|
|
1253
|
-
const details = payload?.details;
|
|
1254
|
-
if (typeof details === "string" && details.includes("InvalidMessageFormat")) return true;
|
|
1255
|
-
if (details && typeof details === "object") {
|
|
1256
|
-
const detailMessage = details.message;
|
|
1257
|
-
if (typeof detailMessage === "string" && detailMessage.includes("InvalidMessageFormat")) return true;
|
|
1258
|
-
}
|
|
1259
|
-
return false;
|
|
1260
|
-
}
|
|
1261
1240
|
function isRetryableBroadcastFailure(response, payload) {
|
|
1262
1241
|
if (response.status >= 500) return true;
|
|
1263
1242
|
const publicationStatus = payload?.publication_status?.status;
|
|
@@ -1279,25 +1258,22 @@ async function postBroadcastPayload2(body, options) {
|
|
|
1279
1258
|
};
|
|
1280
1259
|
}
|
|
1281
1260
|
async function broadcastAlephMessage2(message, options) {
|
|
1282
|
-
const
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
];
|
|
1287
|
-
for (let index = 0; index < attempts.length; index += 1) {
|
|
1288
|
-
const result = await postBroadcastPayload2(attempts[index], {
|
|
1261
|
+
const requestBody = { sync: options.sync ?? false, message };
|
|
1262
|
+
const maxAttempts = 3;
|
|
1263
|
+
for (let index = 0; index < maxAttempts; index += 1) {
|
|
1264
|
+
const result = await postBroadcastPayload2(requestBody, {
|
|
1289
1265
|
apiHost: options.apiHost,
|
|
1290
1266
|
fetch: options.fetch
|
|
1291
1267
|
});
|
|
1292
1268
|
if (result.httpStatus === 202 || normalizeBroadcastStatus(result.httpStatus, result.response?.message_status) !== "unknown" || result.httpStatus >= 200 && result.httpStatus < 300) {
|
|
1293
1269
|
return result;
|
|
1294
1270
|
}
|
|
1295
|
-
const canRetry = index <
|
|
1271
|
+
const canRetry = index < maxAttempts - 1 && isRetryableBroadcastFailure({ status: result.httpStatus }, result.response);
|
|
1296
1272
|
if (!canRetry) {
|
|
1297
1273
|
throw new Error(`Broadcast failed: ${result.httpStatus} ${JSON.stringify(result.response ?? {})}`);
|
|
1298
1274
|
}
|
|
1299
1275
|
}
|
|
1300
|
-
throw new Error("Broadcast failed:
|
|
1276
|
+
throw new Error("Broadcast failed: retry budget exhausted");
|
|
1301
1277
|
}
|
|
1302
1278
|
|
|
1303
1279
|
// ../core/src/forget.ts
|
|
@@ -1664,6 +1640,26 @@ function asString3(value) {
|
|
|
1664
1640
|
function asNumber3(value) {
|
|
1665
1641
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1666
1642
|
}
|
|
1643
|
+
function normalizeIpv6(value) {
|
|
1644
|
+
if (typeof value !== "string") return null;
|
|
1645
|
+
const normalized = value.trim().toLowerCase();
|
|
1646
|
+
if (!normalized) return null;
|
|
1647
|
+
const zoneIndex = normalized.indexOf("%");
|
|
1648
|
+
return zoneIndex >= 0 ? normalized.slice(0, zoneIndex) : normalized;
|
|
1649
|
+
}
|
|
1650
|
+
function isLikelyGlobalIpv6(value) {
|
|
1651
|
+
const normalized = normalizeIpv6(value);
|
|
1652
|
+
if (!normalized || normalized === "::" || normalized === "::1") return false;
|
|
1653
|
+
const firstHextetText = normalized.split(":", 1)[0];
|
|
1654
|
+
if (!firstHextetText) return false;
|
|
1655
|
+
const firstHextet = Number.parseInt(firstHextetText, 16);
|
|
1656
|
+
if (!Number.isFinite(firstHextet)) return false;
|
|
1657
|
+
if ((firstHextet & 65024) === 64512) return false;
|
|
1658
|
+
if ((firstHextet & 65472) === 65152) return false;
|
|
1659
|
+
if ((firstHextet & 65280) === 65280) return false;
|
|
1660
|
+
if ((firstHextet & 65472) === 65216) return false;
|
|
1661
|
+
return true;
|
|
1662
|
+
}
|
|
1667
1663
|
function findCrnByHash(crns, crnHash) {
|
|
1668
1664
|
return crns.find((crn) => crn.hash === crnHash) ?? null;
|
|
1669
1665
|
}
|
|
@@ -1744,10 +1740,22 @@ function describeRuntimeAvailability(runtime) {
|
|
|
1744
1740
|
const execution = runtime.execution ?? null;
|
|
1745
1741
|
const mappedPorts2 = runtime.mappedPorts ?? {};
|
|
1746
1742
|
const hostIpv4 = runtime.hostIpv4 ?? null;
|
|
1743
|
+
const ipv6 = runtime.ipv6 ?? null;
|
|
1747
1744
|
const proxyUrl = runtime.proxyUrl ?? null;
|
|
1748
1745
|
const schedulerSource = runtime.allocation?.source ?? null;
|
|
1749
1746
|
const webAccessActive = runtime.webAccess?.active ?? null;
|
|
1750
1747
|
const mappedPortCount = Object.keys(mappedPorts2).length;
|
|
1748
|
+
if (hostIpv4 && mappedPortCount > 0 && runtime.requirePublicGuestIpv6ForProxy === true && proxyUrl && !isLikelyGlobalIpv6(ipv6)) {
|
|
1749
|
+
return {
|
|
1750
|
+
state: "execution-invalid-public-ipv6",
|
|
1751
|
+
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.`,
|
|
1752
|
+
schedulerSource,
|
|
1753
|
+
executionSeen: Boolean(execution),
|
|
1754
|
+
webAccessActive,
|
|
1755
|
+
mappedPortCount,
|
|
1756
|
+
proxyUrl
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1751
1759
|
if (hostIpv4 && mappedPortCount > 0) {
|
|
1752
1760
|
return {
|
|
1753
1761
|
state: "ready",
|
|
@@ -1864,7 +1872,8 @@ async function fetchVmRuntime(args) {
|
|
|
1864
1872
|
hostIpv4,
|
|
1865
1873
|
ipv6,
|
|
1866
1874
|
proxyUrl,
|
|
1867
|
-
mappedPorts: mappedPorts2
|
|
1875
|
+
mappedPorts: mappedPorts2,
|
|
1876
|
+
requirePublicGuestIpv6ForProxy: args.requirePublicGuestIpv6ForProxy
|
|
1868
1877
|
});
|
|
1869
1878
|
return {
|
|
1870
1879
|
allocation,
|
|
@@ -1888,7 +1897,7 @@ async function waitForVmRuntime(args) {
|
|
|
1888
1897
|
let lastRuntime = await fetchVmRuntime(args);
|
|
1889
1898
|
args.onAttempt?.(lastRuntime, 1, attempts);
|
|
1890
1899
|
for (let attempt = 0; attempt < attempts - 1; attempt += 1) {
|
|
1891
|
-
if (lastRuntime.
|
|
1900
|
+
if (lastRuntime.diagnostics?.state === "ready" || lastRuntime.diagnostics?.state === "execution-invalid-public-ipv6") {
|
|
1892
1901
|
return lastRuntime;
|
|
1893
1902
|
}
|
|
1894
1903
|
await sleep2(delayMs);
|
|
@@ -2562,7 +2571,7 @@ async function waitForRelayBootstrapRegistration(args) {
|
|
|
2562
2571
|
return false;
|
|
2563
2572
|
});
|
|
2564
2573
|
if (match?.content) {
|
|
2565
|
-
|
|
2574
|
+
const visibleRegistration = {
|
|
2566
2575
|
itemHash: match.itemHash ?? null,
|
|
2567
2576
|
hash: match.hash ?? null,
|
|
2568
2577
|
sender: typeof match.address === "string" ? match.address : null,
|
|
@@ -2571,7 +2580,10 @@ async function waitForRelayBootstrapRegistration(args) {
|
|
|
2571
2580
|
multiaddrs: Array.isArray(match.content.multiaddrs) ? match.content.multiaddrs : [],
|
|
2572
2581
|
browserMultiaddrs: Array.isArray(match.content.browserMultiaddrs) ? match.content.browserMultiaddrs : []
|
|
2573
2582
|
};
|
|
2583
|
+
args.onAttempt?.(visibleRegistration, attempt + 1, attempts);
|
|
2584
|
+
return visibleRegistration;
|
|
2574
2585
|
}
|
|
2586
|
+
args.onAttempt?.(null, attempt + 1, attempts);
|
|
2575
2587
|
if (attempt < attempts - 1) {
|
|
2576
2588
|
await sleep(delayMs);
|
|
2577
2589
|
}
|
|
@@ -2684,6 +2696,16 @@ async function publishRelayBootstrapRegistration(args) {
|
|
|
2684
2696
|
|
|
2685
2697
|
// ../core/src/bootstrap-config.ts
|
|
2686
2698
|
var VM_BOOTSTRAP_CONFIG_AGGREGATE_KEY = "vm-bootstrap-config";
|
|
2699
|
+
var VM_BOOTSTRAP_CONFIG_SIGNAL_REF = "vm-bootstrap-config";
|
|
2700
|
+
var VM_BOOTSTRAP_CONFIG_SIGNAL_POST_TYPE = "vm-bootstrap-config-status";
|
|
2701
|
+
function parseJsonObject(value) {
|
|
2702
|
+
try {
|
|
2703
|
+
const parsed = JSON.parse(value);
|
|
2704
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
2705
|
+
} catch {
|
|
2706
|
+
return null;
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2687
2709
|
async function fetchVmBootstrapConfigAggregate(address, options) {
|
|
2688
2710
|
const requestUrl = new URL(`/api/v0/aggregates/${address}.json`, options.apiHost ?? DEFAULT_ALEPH_API_HOST2);
|
|
2689
2711
|
requestUrl.searchParams.set("keys", VM_BOOTSTRAP_CONFIG_AGGREGATE_KEY);
|
|
@@ -2803,6 +2825,89 @@ async function deleteVmBootstrapConfig(args) {
|
|
|
2803
2825
|
aggregate: content
|
|
2804
2826
|
};
|
|
2805
2827
|
}
|
|
2828
|
+
function normalizeVmBootstrapConfigSignalRecord(value) {
|
|
2829
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
2830
|
+
const candidate = value;
|
|
2831
|
+
const deploymentToken = typeof candidate.deploymentToken === "string" ? candidate.deploymentToken.trim() : "";
|
|
2832
|
+
const status = typeof candidate.status === "string" ? candidate.status.trim() : "";
|
|
2833
|
+
const profile = typeof candidate.profile === "string" ? candidate.profile.trim() : "";
|
|
2834
|
+
const ownerAddress = typeof candidate.ownerAddress === "string" ? candidate.ownerAddress.trim() : "";
|
|
2835
|
+
const instanceItemHash = typeof candidate.instanceItemHash === "string" ? candidate.instanceItemHash.trim() : "";
|
|
2836
|
+
const updatedAt = typeof candidate.updatedAt === "string" ? candidate.updatedAt.trim() : "";
|
|
2837
|
+
if (!deploymentToken || !status || !profile || !ownerAddress || !instanceItemHash || !updatedAt) {
|
|
2838
|
+
return null;
|
|
2839
|
+
}
|
|
2840
|
+
return {
|
|
2841
|
+
deploymentToken,
|
|
2842
|
+
status,
|
|
2843
|
+
profile,
|
|
2844
|
+
ownerAddress,
|
|
2845
|
+
instanceItemHash,
|
|
2846
|
+
updatedAt,
|
|
2847
|
+
publisherAddress: typeof candidate.publisherAddress === "string" && candidate.publisherAddress.trim() ? candidate.publisherAddress.trim() : null,
|
|
2848
|
+
authorization: candidate.authorization,
|
|
2849
|
+
peerId: typeof candidate.peerId === "string" && candidate.peerId.trim() ? candidate.peerId.trim() : null,
|
|
2850
|
+
probeMultiaddrs: Array.isArray(candidate.probeMultiaddrs) ? candidate.probeMultiaddrs.filter(
|
|
2851
|
+
(entry) => typeof entry === "string" && entry.trim().length > 0
|
|
2852
|
+
) : [],
|
|
2853
|
+
browserBootstrapMultiaddrs: Array.isArray(candidate.browserBootstrapMultiaddrs) ? candidate.browserBootstrapMultiaddrs.filter(
|
|
2854
|
+
(entry) => typeof entry === "string" && entry.trim().length > 0
|
|
2855
|
+
) : []
|
|
2856
|
+
};
|
|
2857
|
+
}
|
|
2858
|
+
async function fetchVmBootstrapConfigSignals(args) {
|
|
2859
|
+
const requestUrl = new URL("/api/v0/posts.json", args.apiHost ?? DEFAULT_ALEPH_API_HOST2);
|
|
2860
|
+
requestUrl.searchParams.set("refs", args.ref ?? VM_BOOTSTRAP_CONFIG_SIGNAL_REF);
|
|
2861
|
+
requestUrl.searchParams.set("types", args.postType ?? VM_BOOTSTRAP_CONFIG_SIGNAL_POST_TYPE);
|
|
2862
|
+
requestUrl.searchParams.set("pagination", String(Math.max(1, Number(args.pagination ?? 100))));
|
|
2863
|
+
requestUrl.searchParams.set("page", String(Math.max(1, Number(args.page ?? 1))));
|
|
2864
|
+
requestUrl.searchParams.set("sortOrder", "-1");
|
|
2865
|
+
const response = await args.fetch(requestUrl.toString(), { cache: "no-cache" });
|
|
2866
|
+
if (!response.ok) {
|
|
2867
|
+
throw new Error(`VM bootstrap config signal request failed: ${response.status}`);
|
|
2868
|
+
}
|
|
2869
|
+
const payload = await response.json();
|
|
2870
|
+
const expectedDeploymentToken = args.deploymentToken?.trim() ?? "";
|
|
2871
|
+
const expectedOwnerAddress = args.ownerAddress?.trim().toLowerCase() ?? "";
|
|
2872
|
+
const expectedInstanceItemHash = args.instanceItemHash?.trim() ?? "";
|
|
2873
|
+
return (payload.posts ?? []).map((entry) => {
|
|
2874
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
|
|
2875
|
+
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;
|
|
2876
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
2877
|
+
const content = "deploymentToken" in parsed ? parsed : "content" in parsed && parsed.content && typeof parsed.content === "object" && !Array.isArray(parsed.content) ? parsed.content : null;
|
|
2878
|
+
if (!content || typeof content !== "object" || Array.isArray(content)) return null;
|
|
2879
|
+
return normalizeVmBootstrapConfigSignalRecord(content);
|
|
2880
|
+
}).filter((entry) => {
|
|
2881
|
+
if (!entry) return false;
|
|
2882
|
+
if (expectedDeploymentToken && entry.deploymentToken !== expectedDeploymentToken) return false;
|
|
2883
|
+
if (expectedOwnerAddress && entry.ownerAddress.toLowerCase() !== expectedOwnerAddress) return false;
|
|
2884
|
+
if (expectedInstanceItemHash && entry.instanceItemHash !== expectedInstanceItemHash) return false;
|
|
2885
|
+
return true;
|
|
2886
|
+
});
|
|
2887
|
+
}
|
|
2888
|
+
async function waitForVmBootstrapConfigSignal(args) {
|
|
2889
|
+
const attempts = Math.max(1, Number(args.attempts ?? 24));
|
|
2890
|
+
const delayMs = Math.max(0, Number(args.delayMs ?? 2500));
|
|
2891
|
+
const sleep2 = args.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
2892
|
+
const expectedStatus = args.expectedStatus ?? "applied";
|
|
2893
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
2894
|
+
const signals = await fetchVmBootstrapConfigSignals({
|
|
2895
|
+
deploymentToken: args.deploymentToken,
|
|
2896
|
+
ownerAddress: args.ownerAddress,
|
|
2897
|
+
instanceItemHash: args.instanceItemHash,
|
|
2898
|
+
fetch: args.fetch,
|
|
2899
|
+
apiHost: args.apiHost,
|
|
2900
|
+
ref: args.ref,
|
|
2901
|
+
postType: args.postType
|
|
2902
|
+
}).catch(() => []);
|
|
2903
|
+
const match = signals.find((entry) => entry.status === expectedStatus) ?? null;
|
|
2904
|
+
if (match) return match;
|
|
2905
|
+
if (attempt < attempts - 1) {
|
|
2906
|
+
await sleep2(delayMs);
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
return null;
|
|
2910
|
+
}
|
|
2806
2911
|
|
|
2807
2912
|
// ../core/src/bootstrap-reconcile.ts
|
|
2808
2913
|
function createRelayBootstrapRegistrationId(profile, instanceName, itemHash) {
|
|
@@ -2944,7 +3049,6 @@ function defaultState(props = {}) {
|
|
|
2944
3049
|
instances: [],
|
|
2945
3050
|
bootstrapRegistrations: [],
|
|
2946
3051
|
orphanBootstrapRegistrations: [],
|
|
2947
|
-
relayPing: RELAY_PING_IDLE_STATE,
|
|
2948
3052
|
lastDeploymentHash: null,
|
|
2949
3053
|
deploymentProgress: IDLE_DEPLOYMENT_PROGRESS
|
|
2950
3054
|
};
|
|
@@ -3171,57 +3275,6 @@ function compatibleCrnsForTier(crns, state) {
|
|
|
3171
3275
|
);
|
|
3172
3276
|
return filterDeployableCrns(crns, { spec });
|
|
3173
3277
|
}
|
|
3174
|
-
async function pingPeer(libp2p) {
|
|
3175
|
-
if (!libp2p || typeof libp2p !== "object") {
|
|
3176
|
-
return RELAY_PING_IDLE_STATE;
|
|
3177
|
-
}
|
|
3178
|
-
const candidate = libp2p;
|
|
3179
|
-
const peers = candidate.getPeers?.() ?? [];
|
|
3180
|
-
const firstPeer = peers[0];
|
|
3181
|
-
if (!firstPeer) {
|
|
3182
|
-
return {
|
|
3183
|
-
...RELAY_PING_IDLE_STATE,
|
|
3184
|
-
tone: "caution",
|
|
3185
|
-
error: "No connected relay peers available."
|
|
3186
|
-
};
|
|
3187
|
-
}
|
|
3188
|
-
const sentAt = Date.now();
|
|
3189
|
-
try {
|
|
3190
|
-
const pingFn = candidate.services?.ping?.ping?.bind(candidate.services.ping) ?? candidate.ping?.bind(candidate);
|
|
3191
|
-
if (!pingFn) {
|
|
3192
|
-
return {
|
|
3193
|
-
...RELAY_PING_IDLE_STATE,
|
|
3194
|
-
tone: "caution",
|
|
3195
|
-
sent: true,
|
|
3196
|
-
lastPeerId: String(firstPeer),
|
|
3197
|
-
lastSentAt: sentAt,
|
|
3198
|
-
error: "libp2p ping service not available."
|
|
3199
|
-
};
|
|
3200
|
-
}
|
|
3201
|
-
const latency = await pingFn(firstPeer);
|
|
3202
|
-
return {
|
|
3203
|
-
tone: "ok",
|
|
3204
|
-
sent: true,
|
|
3205
|
-
received: true,
|
|
3206
|
-
lastPeerId: String(firstPeer),
|
|
3207
|
-
lastLatencyMs: Number(latency),
|
|
3208
|
-
lastSentAt: sentAt,
|
|
3209
|
-
lastReceivedAt: Date.now(),
|
|
3210
|
-
error: null
|
|
3211
|
-
};
|
|
3212
|
-
} catch (error) {
|
|
3213
|
-
return {
|
|
3214
|
-
tone: "error",
|
|
3215
|
-
sent: true,
|
|
3216
|
-
received: false,
|
|
3217
|
-
lastPeerId: String(firstPeer),
|
|
3218
|
-
lastLatencyMs: null,
|
|
3219
|
-
lastSentAt: sentAt,
|
|
3220
|
-
lastReceivedAt: null,
|
|
3221
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3222
|
-
};
|
|
3223
|
-
}
|
|
3224
|
-
}
|
|
3225
3278
|
var UI_DEPLOY_WAIT_ATTEMPTS = 60;
|
|
3226
3279
|
var UI_DEPLOY_WAIT_DELAY_MS = 5e3;
|
|
3227
3280
|
var UI_RUNTIME_WAIT_ATTEMPTS = 40;
|
|
@@ -3252,7 +3305,6 @@ var SponsorRelayController = class {
|
|
|
3252
3305
|
subscribers = /* @__PURE__ */ new Set();
|
|
3253
3306
|
client;
|
|
3254
3307
|
refreshTimer = null;
|
|
3255
|
-
pingTimer = null;
|
|
3256
3308
|
manifestRefreshTimer = null;
|
|
3257
3309
|
stopWalletWatch = null;
|
|
3258
3310
|
props;
|
|
@@ -3308,8 +3360,7 @@ var SponsorRelayController = class {
|
|
|
3308
3360
|
...patch,
|
|
3309
3361
|
busy: patch.busy ? { ...this.state.busy, ...patch.busy } : this.state.busy,
|
|
3310
3362
|
wallet: patch.wallet ? { ...this.state.wallet, ...patch.wallet } : this.state.wallet,
|
|
3311
|
-
pricingSummary: patch.pricingSummary ? { ...this.state.pricingSummary, ...patch.pricingSummary } : this.state.pricingSummary
|
|
3312
|
-
relayPing: patch.relayPing ? { ...this.state.relayPing, ...patch.relayPing } : this.state.relayPing
|
|
3363
|
+
pricingSummary: patch.pricingSummary ? { ...this.state.pricingSummary, ...patch.pricingSummary } : this.state.pricingSummary
|
|
3313
3364
|
};
|
|
3314
3365
|
this.emit();
|
|
3315
3366
|
}
|
|
@@ -3342,11 +3393,13 @@ var SponsorRelayController = class {
|
|
|
3342
3393
|
if (!args.runtime.hostIpv4) {
|
|
3343
3394
|
throw new Error("Relay runtime did not expose a host IPv4 address.");
|
|
3344
3395
|
}
|
|
3345
|
-
|
|
3396
|
+
let runtime = args.runtime;
|
|
3397
|
+
let mappedPorts2 = runtime.mappedPorts ?? {};
|
|
3346
3398
|
const setupPort = mappedPorts2["80"]?.host ?? null;
|
|
3347
3399
|
if (!setupPort) {
|
|
3348
3400
|
throw new Error("Relay runtime did not expose the temporary setup endpoint.");
|
|
3349
3401
|
}
|
|
3402
|
+
let bootstrapSignalRelayMetadata = null;
|
|
3350
3403
|
if (profile === "orbitdb-relay-pinner") {
|
|
3351
3404
|
this.emitProgress({
|
|
3352
3405
|
stage: "deployment-confirmed",
|
|
@@ -3362,7 +3415,17 @@ var SponsorRelayController = class {
|
|
|
3362
3415
|
fetch: (url, init) => fetch(url, init),
|
|
3363
3416
|
attempts: 15,
|
|
3364
3417
|
delayMs: 4e3,
|
|
3365
|
-
httpTimeoutMs: 1e4
|
|
3418
|
+
httpTimeoutMs: 1e4,
|
|
3419
|
+
onAttempt: (result, attempt, attempts) => {
|
|
3420
|
+
this.emitProgress({
|
|
3421
|
+
stage: "deployment-confirmed",
|
|
3422
|
+
label: "Waiting for guest setup",
|
|
3423
|
+
progress: 93,
|
|
3424
|
+
status: result.ok ? "success" : "info",
|
|
3425
|
+
itemHash: args.itemHash,
|
|
3426
|
+
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.`
|
|
3427
|
+
});
|
|
3428
|
+
}
|
|
3366
3429
|
});
|
|
3367
3430
|
if (!setupHealth.ok) {
|
|
3368
3431
|
throw new Error(
|
|
@@ -3405,6 +3468,57 @@ var SponsorRelayController = class {
|
|
|
3405
3468
|
if (!args.deploymentToken) {
|
|
3406
3469
|
throw new Error("Missing deployment token for Aleph bootstrap config handoff.");
|
|
3407
3470
|
}
|
|
3471
|
+
if (runtime.proxyUrl && runtime.webAccess?.active !== true) {
|
|
3472
|
+
this.emitProgress({
|
|
3473
|
+
stage: "deployment-confirmed",
|
|
3474
|
+
label: "Waiting for HTTPS route",
|
|
3475
|
+
progress: 93,
|
|
3476
|
+
status: "warning",
|
|
3477
|
+
itemHash: args.itemHash,
|
|
3478
|
+
detail: "The relay received a 2n6 hostname, but it is not active yet. Waiting before handing HTTPS config to the guest."
|
|
3479
|
+
});
|
|
3480
|
+
let latestRuntime = runtime;
|
|
3481
|
+
for (let attempt = 0; attempt < UI_RUNTIME_WAIT_ATTEMPTS; attempt += 1) {
|
|
3482
|
+
if (latestRuntime.webAccess?.active === true) {
|
|
3483
|
+
break;
|
|
3484
|
+
}
|
|
3485
|
+
await new Promise(
|
|
3486
|
+
(resolve) => globalThis.setTimeout(resolve, UI_RUNTIME_WAIT_DELAY_MS)
|
|
3487
|
+
);
|
|
3488
|
+
latestRuntime = await fetchVmRuntime({
|
|
3489
|
+
itemHash: args.itemHash,
|
|
3490
|
+
fetch: (url, init) => fetch(url, init),
|
|
3491
|
+
crnHash: runtime.selectedCrn?.hash ?? runtime.allocation?.crnHash ?? void 0,
|
|
3492
|
+
crns: this.state.crns,
|
|
3493
|
+
crnListUrl: this.props.crnListUrl,
|
|
3494
|
+
requirePublicGuestIpv6ForProxy: true
|
|
3495
|
+
}).catch(() => latestRuntime);
|
|
3496
|
+
this.emitProgress({
|
|
3497
|
+
stage: "deployment-confirmed",
|
|
3498
|
+
label: "Waiting for HTTPS route",
|
|
3499
|
+
progress: 93,
|
|
3500
|
+
status: latestRuntime.webAccess?.active === true ? "info" : "warning",
|
|
3501
|
+
itemHash: args.itemHash,
|
|
3502
|
+
detail: `2n6 activation ${attempt + 1}/${UI_RUNTIME_WAIT_ATTEMPTS}: ${latestRuntime.webAccess?.active === true ? "active" : "still pending"} for ${latestRuntime.proxyUrl ?? runtime.proxyUrl ?? "reserved proxy URL"}.`
|
|
3503
|
+
});
|
|
3504
|
+
this.trace("deploy:bootstrap-proxy-activation", {
|
|
3505
|
+
itemHash: args.itemHash,
|
|
3506
|
+
attempt: attempt + 1,
|
|
3507
|
+
attempts: UI_RUNTIME_WAIT_ATTEMPTS,
|
|
3508
|
+
active: latestRuntime.webAccess?.active === true,
|
|
3509
|
+
proxyUrl: latestRuntime.proxyUrl ?? null
|
|
3510
|
+
});
|
|
3511
|
+
}
|
|
3512
|
+
runtime = latestRuntime;
|
|
3513
|
+
mappedPorts2 = runtime.mappedPorts ?? mappedPorts2;
|
|
3514
|
+
}
|
|
3515
|
+
if (!runtime.hostIpv4) {
|
|
3516
|
+
throw new Error(
|
|
3517
|
+
"Relay runtime lost its host IPv4 address while waiting for HTTPS activation."
|
|
3518
|
+
);
|
|
3519
|
+
}
|
|
3520
|
+
const runtimeHostIpv4 = runtime.hostIpv4;
|
|
3521
|
+
const activeProxyUrl = runtime.webAccess?.active === true ? runtime.proxyUrl ?? null : null;
|
|
3408
3522
|
this.emitProgress({
|
|
3409
3523
|
stage: "deployment-confirmed",
|
|
3410
3524
|
label: "Publishing relay config",
|
|
@@ -3422,9 +3536,9 @@ var SponsorRelayController = class {
|
|
|
3422
3536
|
expiresAt: new Date(Date.now() + 30 * 6e4).toISOString(),
|
|
3423
3537
|
status: "pending",
|
|
3424
3538
|
runtime: {
|
|
3425
|
-
publicIpv4:
|
|
3426
|
-
publicIpv6:
|
|
3427
|
-
proxyUrl:
|
|
3539
|
+
publicIpv4: runtimeHostIpv4,
|
|
3540
|
+
publicIpv6: runtime.ipv6 ?? null,
|
|
3541
|
+
proxyUrl: activeProxyUrl,
|
|
3428
3542
|
mappedPorts: mappedPorts2
|
|
3429
3543
|
},
|
|
3430
3544
|
bootstrap: {
|
|
@@ -3453,46 +3567,120 @@ var SponsorRelayController = class {
|
|
|
3453
3567
|
aggregateItemHash: bootstrapConfigPublication.aggregateItemHash,
|
|
3454
3568
|
aggregateStatus: bootstrapConfigPublication.aggregateStatus
|
|
3455
3569
|
});
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3570
|
+
this.emitProgress({
|
|
3571
|
+
stage: "deployment-confirmed",
|
|
3572
|
+
label: "Waiting for guest config",
|
|
3573
|
+
progress: 94,
|
|
3574
|
+
status: "info",
|
|
3575
|
+
itemHash: args.itemHash,
|
|
3576
|
+
detail: "Waiting for the relay VM to acknowledge that it consumed the Aleph bootstrap config."
|
|
3577
|
+
});
|
|
3578
|
+
const bootstrapConfigSignal = await waitForVmBootstrapConfigSignal({
|
|
3579
|
+
deploymentToken: args.deploymentToken,
|
|
3580
|
+
ownerAddress: this.state.wallet.address,
|
|
3581
|
+
instanceItemHash: args.itemHash,
|
|
3582
|
+
fetch: asJsonFetch,
|
|
3583
|
+
apiHost: this.client.apiHost,
|
|
3584
|
+
attempts: 40,
|
|
3585
|
+
delayMs: 3e3
|
|
3586
|
+
});
|
|
3587
|
+
this.trace("deploy:bootstrap-config-signal-visible", {
|
|
3588
|
+
itemHash: args.itemHash,
|
|
3589
|
+
deploymentToken: args.deploymentToken,
|
|
3590
|
+
bootstrapConfigSignal
|
|
3591
|
+
});
|
|
3592
|
+
if (!bootstrapConfigSignal) {
|
|
3593
|
+
throw new Error(
|
|
3594
|
+
"The relay VM did not confirm that it applied the Aleph bootstrap config in time."
|
|
3595
|
+
);
|
|
3596
|
+
}
|
|
3597
|
+
bootstrapSignalRelayMetadata = typeof bootstrapConfigSignal.peerId === "string" && bootstrapConfigSignal.peerId.length > 0 && Array.isArray(bootstrapConfigSignal.probeMultiaddrs) && bootstrapConfigSignal.probeMultiaddrs.length > 0 ? {
|
|
3598
|
+
peerId: bootstrapConfigSignal.peerId,
|
|
3599
|
+
probeMultiaddrs: bootstrapConfigSignal.probeMultiaddrs,
|
|
3600
|
+
browserBootstrapMultiaddrs: bootstrapConfigSignal.browserBootstrapMultiaddrs ?? []
|
|
3601
|
+
} : null;
|
|
3602
|
+
if (runtime.proxyUrl && runtime.webAccess?.active !== true) {
|
|
3603
|
+
this.trace("deploy:bootstrap-proxy-fallback", {
|
|
3604
|
+
itemHash: args.itemHash,
|
|
3605
|
+
deploymentToken: args.deploymentToken,
|
|
3606
|
+
proxyUrl: runtime.proxyUrl,
|
|
3607
|
+
bootstrapSignalRelayMetadata
|
|
3608
|
+
});
|
|
3609
|
+
this.emitProgress({
|
|
3610
|
+
stage: "deployment-confirmed",
|
|
3611
|
+
label: "Continuing without HTTPS route",
|
|
3612
|
+
progress: 94,
|
|
3613
|
+
status: "warning",
|
|
3476
3614
|
itemHash: args.itemHash,
|
|
3477
|
-
|
|
3478
|
-
attempts: result.attempts,
|
|
3479
|
-
requestUrl: result.requestUrl,
|
|
3480
|
-
metadataUrl: result.metadataUrl,
|
|
3481
|
-
hostIpv4: result.hostIpv4,
|
|
3482
|
-
setupPort: result.setupPort,
|
|
3483
|
-
ok: result.ok,
|
|
3484
|
-
status: result.status,
|
|
3485
|
-
ready: result.ready,
|
|
3486
|
-
error: result.error ?? null,
|
|
3487
|
-
payloadStatus: payload && typeof payload.status === "string" ? payload.status : null,
|
|
3488
|
-
peerId: metadata && typeof metadata.peer_id === "string" ? metadata.peer_id : null,
|
|
3489
|
-
probeMultiaddrCount: Array.isArray(metadata?.probe_multiaddrs) ? metadata.probe_multiaddrs.length : 0,
|
|
3490
|
-
browserBootstrapMultiaddrCount: Array.isArray(
|
|
3491
|
-
metadata?.browser_bootstrap_multiaddrs
|
|
3492
|
-
) ? metadata.browser_bootstrap_multiaddrs.length : 0
|
|
3615
|
+
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."
|
|
3493
3616
|
});
|
|
3617
|
+
runtime = {
|
|
3618
|
+
...runtime,
|
|
3619
|
+
proxyUrl: null,
|
|
3620
|
+
webAccess: runtime.webAccess ? {
|
|
3621
|
+
...runtime.webAccess,
|
|
3622
|
+
active: false
|
|
3623
|
+
} : runtime.webAccess
|
|
3624
|
+
};
|
|
3494
3625
|
}
|
|
3495
|
-
}
|
|
3626
|
+
}
|
|
3627
|
+
let relayMetadata = bootstrapSignalRelayMetadata;
|
|
3628
|
+
if (relayMetadata) {
|
|
3629
|
+
this.trace("deploy:relay-metadata-from-bootstrap-signal", {
|
|
3630
|
+
itemHash: args.itemHash,
|
|
3631
|
+
peerId: relayMetadata.peerId,
|
|
3632
|
+
probeMultiaddrCount: relayMetadata.probeMultiaddrs.length,
|
|
3633
|
+
browserBootstrapMultiaddrCount: relayMetadata.browserBootstrapMultiaddrs.length
|
|
3634
|
+
});
|
|
3635
|
+
} else {
|
|
3636
|
+
this.emitProgress({
|
|
3637
|
+
stage: "publishing-bootstrap",
|
|
3638
|
+
label: "Waiting for secure relay metadata",
|
|
3639
|
+
progress: 95,
|
|
3640
|
+
status: "warning",
|
|
3641
|
+
itemHash: args.itemHash,
|
|
3642
|
+
detail: "Runtime networking is available. Waiting for the secure relay metadata endpoint to report final public multiaddrs."
|
|
3643
|
+
});
|
|
3644
|
+
relayMetadata = await fetchRelayMetadataForRuntime({
|
|
3645
|
+
runtime,
|
|
3646
|
+
fetch: (url, init) => fetch(url, init),
|
|
3647
|
+
preferSecureMetadata: profile === "uc-go-peer" && Boolean(runtime.proxyUrl),
|
|
3648
|
+
attempts: 80,
|
|
3649
|
+
delayMs: 3e3,
|
|
3650
|
+
timeoutMs: 24e4,
|
|
3651
|
+
onAttempt: (result) => {
|
|
3652
|
+
const payload = result.payload && typeof result.payload === "object" ? result.payload : null;
|
|
3653
|
+
const metadata = payload?.metadata && typeof payload.metadata === "object" ? payload.metadata : null;
|
|
3654
|
+
this.trace("deploy:relay-metadata-attempt", {
|
|
3655
|
+
itemHash: args.itemHash,
|
|
3656
|
+
attempt: result.attempt,
|
|
3657
|
+
attempts: result.attempts,
|
|
3658
|
+
requestUrl: result.requestUrl,
|
|
3659
|
+
metadataUrl: result.metadataUrl,
|
|
3660
|
+
hostIpv4: result.hostIpv4,
|
|
3661
|
+
setupPort: result.setupPort,
|
|
3662
|
+
ok: result.ok,
|
|
3663
|
+
status: result.status,
|
|
3664
|
+
ready: result.ready,
|
|
3665
|
+
error: result.error ?? null,
|
|
3666
|
+
payloadStatus: payload && typeof payload.status === "string" ? payload.status : null,
|
|
3667
|
+
peerId: metadata && typeof metadata.peer_id === "string" ? metadata.peer_id : null,
|
|
3668
|
+
probeMultiaddrCount: Array.isArray(metadata?.probe_multiaddrs) ? metadata.probe_multiaddrs.length : 0,
|
|
3669
|
+
browserBootstrapMultiaddrCount: Array.isArray(
|
|
3670
|
+
metadata?.browser_bootstrap_multiaddrs
|
|
3671
|
+
) ? metadata.browser_bootstrap_multiaddrs.length : 0
|
|
3672
|
+
});
|
|
3673
|
+
this.emitProgress({
|
|
3674
|
+
stage: "publishing-bootstrap",
|
|
3675
|
+
label: result.ready ? "Secure relay metadata ready" : "Waiting for secure relay metadata",
|
|
3676
|
+
progress: 95,
|
|
3677
|
+
status: result.ready ? "success" : "warning",
|
|
3678
|
+
itemHash: args.itemHash,
|
|
3679
|
+
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}.`
|
|
3680
|
+
});
|
|
3681
|
+
}
|
|
3682
|
+
});
|
|
3683
|
+
}
|
|
3496
3684
|
if (!relayMetadata) {
|
|
3497
3685
|
throw new Error("Relay metadata did not include a peer ID and public multiaddrs.");
|
|
3498
3686
|
}
|
|
@@ -3516,7 +3704,17 @@ var SponsorRelayController = class {
|
|
|
3516
3704
|
fetch: asJsonFetch,
|
|
3517
3705
|
apiHost: this.client.apiHost,
|
|
3518
3706
|
attempts: 24,
|
|
3519
|
-
delayMs: 2500
|
|
3707
|
+
delayMs: 2500,
|
|
3708
|
+
onAttempt: (match, attempt, attempts) => {
|
|
3709
|
+
this.emitProgress({
|
|
3710
|
+
stage: "publishing-bootstrap",
|
|
3711
|
+
label: "Waiting for guest bootstrap registration",
|
|
3712
|
+
progress: 97,
|
|
3713
|
+
status: match ? "success" : "info",
|
|
3714
|
+
itemHash: args.itemHash,
|
|
3715
|
+
detail: match ? `Guest bootstrap registration ${attempt}/${attempts}: visible on Aleph.` : `Guest bootstrap registration ${attempt}/${attempts}: still waiting for the relay VM registration on Aleph.`
|
|
3716
|
+
});
|
|
3717
|
+
}
|
|
3520
3718
|
});
|
|
3521
3719
|
this.trace("deploy:bootstrap-registration-visible", {
|
|
3522
3720
|
itemHash: args.itemHash,
|
|
@@ -3567,7 +3765,17 @@ var SponsorRelayController = class {
|
|
|
3567
3765
|
fetch: asJsonFetch,
|
|
3568
3766
|
apiHost: this.client.apiHost,
|
|
3569
3767
|
attempts: 24,
|
|
3570
|
-
delayMs: 2500
|
|
3768
|
+
delayMs: 2500,
|
|
3769
|
+
onAttempt: (match, attempt, attempts) => {
|
|
3770
|
+
this.emitProgress({
|
|
3771
|
+
stage: "publishing-bootstrap",
|
|
3772
|
+
label: "Waiting for bootstrap fallback",
|
|
3773
|
+
progress: 98,
|
|
3774
|
+
status: match ? "success" : "warning",
|
|
3775
|
+
itemHash: args.itemHash,
|
|
3776
|
+
detail: match ? `Bootstrap fallback ${attempt}/${attempts}: visible on Aleph.` : `Bootstrap fallback ${attempt}/${attempts}: waiting for the browser-published registration on Aleph.`
|
|
3777
|
+
});
|
|
3778
|
+
}
|
|
3571
3779
|
});
|
|
3572
3780
|
this.trace("deploy:bootstrap-registration-fallback-visible", {
|
|
3573
3781
|
itemHash: args.itemHash,
|
|
@@ -3613,6 +3821,15 @@ var SponsorRelayController = class {
|
|
|
3613
3821
|
const currentProgress = this.state.deploymentProgress;
|
|
3614
3822
|
const currentProgressIsForLatestHash = currentProgress.itemHash === itemHash || currentProgress.itemHash == null && currentProgress.stage !== "idle" && currentProgress.stage !== "error";
|
|
3615
3823
|
const keepTerminalSuccess = this.lastCompletedDeploymentHash === itemHash || currentProgressIsForLatestHash && currentProgress.stage === "completed" && currentProgress.status === "success";
|
|
3824
|
+
if (currentProgressIsForLatestHash && currentProgress.stage === "publishing-bootstrap") {
|
|
3825
|
+
this.trace("progress:retain-active-phase", {
|
|
3826
|
+
reason: "latest refresh would overwrite active relay metadata/bootstrap progress",
|
|
3827
|
+
itemHash,
|
|
3828
|
+
label: currentProgress.label,
|
|
3829
|
+
progress: currentProgress.progress
|
|
3830
|
+
});
|
|
3831
|
+
return;
|
|
3832
|
+
}
|
|
3616
3833
|
const status = latest.details.messageStatus;
|
|
3617
3834
|
if (status === "rejected") {
|
|
3618
3835
|
this.emitProgress({
|
|
@@ -3743,16 +3960,11 @@ var SponsorRelayController = class {
|
|
|
3743
3960
|
this.refreshTimer = setInterval(() => {
|
|
3744
3961
|
void this.refresh();
|
|
3745
3962
|
}, REFRESH_INTERVAL_MS);
|
|
3746
|
-
this.pingTimer = setInterval(() => {
|
|
3747
|
-
void this.refreshRelayPing();
|
|
3748
|
-
}, RELAY_PING_INTERVAL_MS);
|
|
3749
|
-
await this.refreshRelayPing();
|
|
3750
3963
|
this.patch({ ready: true });
|
|
3751
3964
|
this.trace("init:ready");
|
|
3752
3965
|
}
|
|
3753
3966
|
destroy() {
|
|
3754
3967
|
if (this.refreshTimer) clearInterval(this.refreshTimer);
|
|
3755
|
-
if (this.pingTimer) clearInterval(this.pingTimer);
|
|
3756
3968
|
if (this.manifestRefreshTimer) clearTimeout(this.manifestRefreshTimer);
|
|
3757
3969
|
this.stopWalletWatch?.();
|
|
3758
3970
|
}
|
|
@@ -4047,11 +4259,6 @@ var SponsorRelayController = class {
|
|
|
4047
4259
|
});
|
|
4048
4260
|
}
|
|
4049
4261
|
}
|
|
4050
|
-
async refreshRelayPing() {
|
|
4051
|
-
const relayPing = await pingPeer(this.props.libp2p);
|
|
4052
|
-
this.patch({ relayPing });
|
|
4053
|
-
this.trace("libp2p:relay-ping", relayPing);
|
|
4054
|
-
}
|
|
4055
4262
|
async deploy() {
|
|
4056
4263
|
this.trace("deploy:start", {
|
|
4057
4264
|
wallet: this.state.wallet.address,
|
|
@@ -4168,7 +4375,7 @@ var SponsorRelayController = class {
|
|
|
4168
4375
|
fetch: (url, init) => fetch(url, init),
|
|
4169
4376
|
attempts: UI_DEPLOY_WAIT_ATTEMPTS,
|
|
4170
4377
|
delayMs: UI_DEPLOY_WAIT_DELAY_MS,
|
|
4171
|
-
onAttempt: (inspectionResult) => {
|
|
4378
|
+
onAttempt: (inspectionResult, attempt, attempts) => {
|
|
4172
4379
|
if (inspectionResult.status === "processed") {
|
|
4173
4380
|
this.emitProgress({
|
|
4174
4381
|
stage: "deployment-confirmed",
|
|
@@ -4176,7 +4383,7 @@ var SponsorRelayController = class {
|
|
|
4176
4383
|
progress: 82,
|
|
4177
4384
|
status: "success",
|
|
4178
4385
|
itemHash: result.itemHash,
|
|
4179
|
-
detail:
|
|
4386
|
+
detail: `Aleph processing ${attempt}/${attempts}: instance message processed.`
|
|
4180
4387
|
});
|
|
4181
4388
|
return;
|
|
4182
4389
|
}
|
|
@@ -4198,7 +4405,7 @@ var SponsorRelayController = class {
|
|
|
4198
4405
|
progress: 76,
|
|
4199
4406
|
status: "info",
|
|
4200
4407
|
itemHash: result.itemHash,
|
|
4201
|
-
detail:
|
|
4408
|
+
detail: `Aleph processing ${attempt}/${attempts}: waiting for the instance message to be processed.`
|
|
4202
4409
|
});
|
|
4203
4410
|
}
|
|
4204
4411
|
});
|
|
@@ -4262,17 +4469,29 @@ var SponsorRelayController = class {
|
|
|
4262
4469
|
crnHash: candidateCrn.hash,
|
|
4263
4470
|
crns: this.state.crns,
|
|
4264
4471
|
crnListUrl: this.props.crnListUrl,
|
|
4472
|
+
requirePublicGuestIpv6ForProxy: true,
|
|
4265
4473
|
attempts: UI_RUNTIME_WAIT_ATTEMPTS,
|
|
4266
4474
|
delayMs: UI_RUNTIME_WAIT_DELAY_MS,
|
|
4267
|
-
onAttempt: (runtime2) => {
|
|
4268
|
-
if (runtime2.
|
|
4475
|
+
onAttempt: (runtime2, attempt, attempts) => {
|
|
4476
|
+
if (runtime2.diagnostics?.state === "ready") {
|
|
4269
4477
|
this.emitProgress({
|
|
4270
4478
|
stage: "deployment-confirmed",
|
|
4271
4479
|
label: "Runtime ready",
|
|
4272
4480
|
progress: 92,
|
|
4273
4481
|
status: "info",
|
|
4274
4482
|
itemHash: result.itemHash,
|
|
4275
|
-
detail:
|
|
4483
|
+
detail: `Runtime ${attempt}/${attempts}: networking and mapped ports are now available.`
|
|
4484
|
+
});
|
|
4485
|
+
return;
|
|
4486
|
+
}
|
|
4487
|
+
if (runtime2.diagnostics?.state === "execution-invalid-public-ipv6") {
|
|
4488
|
+
this.emitProgress({
|
|
4489
|
+
stage: "deployment-confirmed",
|
|
4490
|
+
label: "Rejecting unusable runtime",
|
|
4491
|
+
progress: 91,
|
|
4492
|
+
status: "warning",
|
|
4493
|
+
itemHash: result.itemHash,
|
|
4494
|
+
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.`
|
|
4276
4495
|
});
|
|
4277
4496
|
return;
|
|
4278
4497
|
}
|
|
@@ -4282,11 +4501,11 @@ var SponsorRelayController = class {
|
|
|
4282
4501
|
progress: 90,
|
|
4283
4502
|
status: "warning",
|
|
4284
4503
|
itemHash: result.itemHash,
|
|
4285
|
-
detail: runtime2.diagnostics?.reason
|
|
4504
|
+
detail: runtime2.diagnostics?.reason ? `Runtime ${attempt}/${attempts}: ${runtime2.diagnostics.reason}` : `Runtime ${attempt}/${attempts}: waiting for CRN runtime networking and mapped ports.`
|
|
4286
4505
|
});
|
|
4287
4506
|
}
|
|
4288
4507
|
});
|
|
4289
|
-
if (
|
|
4508
|
+
if (runtime.diagnostics?.state !== "ready") {
|
|
4290
4509
|
throw new Error(
|
|
4291
4510
|
runtime.diagnostics?.reason ?? "Deployment was processed, but runtime networking never exposed mapped ports."
|
|
4292
4511
|
);
|
|
@@ -4335,6 +4554,7 @@ var SponsorRelayController = class {
|
|
|
4335
4554
|
} catch (error) {
|
|
4336
4555
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
4337
4556
|
const attemptLabel = crnDisplayName;
|
|
4557
|
+
const failedDueToInvalidIpv6 = /not globally routable|public guest IPv6/i.test(lastError.message);
|
|
4338
4558
|
attemptErrors.push(`${attemptLabel}: ${lastError.message}`);
|
|
4339
4559
|
this.trace("deploy:crn-attempt-error", {
|
|
4340
4560
|
crnHash: candidateCrn.hash,
|
|
@@ -4343,7 +4563,23 @@ var SponsorRelayController = class {
|
|
|
4343
4563
|
error: lastError.message
|
|
4344
4564
|
});
|
|
4345
4565
|
if (attemptItemHash) {
|
|
4566
|
+
this.emitProgress({
|
|
4567
|
+
stage: "deployment-confirmed",
|
|
4568
|
+
label: failedDueToInvalidIpv6 ? "Rejecting unusable runtime" : "Cleaning up failed attempt",
|
|
4569
|
+
progress: failedDueToInvalidIpv6 ? 91 : 16,
|
|
4570
|
+
status: "warning",
|
|
4571
|
+
itemHash: attemptItemHash,
|
|
4572
|
+
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.`
|
|
4573
|
+
});
|
|
4346
4574
|
if (attemptDeploymentToken) {
|
|
4575
|
+
this.emitProgress({
|
|
4576
|
+
stage: "deployment-confirmed",
|
|
4577
|
+
label: "Cleaning up failed attempt",
|
|
4578
|
+
progress: 17,
|
|
4579
|
+
status: "warning",
|
|
4580
|
+
itemHash: attemptItemHash,
|
|
4581
|
+
detail: "Removing bootstrap configuration for the failed attempt. MetaMask may request a cleanup signature."
|
|
4582
|
+
});
|
|
4347
4583
|
this.trace("deploy:bootstrap-config-cleanup-start", {
|
|
4348
4584
|
itemHash: attemptItemHash,
|
|
4349
4585
|
deploymentToken: attemptDeploymentToken
|
|
@@ -4371,6 +4607,14 @@ var SponsorRelayController = class {
|
|
|
4371
4607
|
});
|
|
4372
4608
|
});
|
|
4373
4609
|
}
|
|
4610
|
+
this.emitProgress({
|
|
4611
|
+
stage: "deployment-confirmed",
|
|
4612
|
+
label: "Cleaning up failed attempt",
|
|
4613
|
+
progress: 18,
|
|
4614
|
+
status: "warning",
|
|
4615
|
+
itemHash: attemptItemHash,
|
|
4616
|
+
detail: "Forgetting the failed deployment attempt on Aleph. MetaMask may request another cleanup signature."
|
|
4617
|
+
});
|
|
4374
4618
|
this.trace("deploy:forget-failed-attempt-start", {
|
|
4375
4619
|
itemHash: attemptItemHash,
|
|
4376
4620
|
crnHash: candidateCrn.hash,
|
|
@@ -4403,11 +4647,11 @@ var SponsorRelayController = class {
|
|
|
4403
4647
|
if (candidateIndex < orderedCrns.length - 1) {
|
|
4404
4648
|
this.emitProgress({
|
|
4405
4649
|
stage: "selecting-crn",
|
|
4406
|
-
label:
|
|
4650
|
+
label: "Retrying on next CRN",
|
|
4407
4651
|
progress: 20,
|
|
4408
4652
|
status: "warning",
|
|
4409
4653
|
itemHash: attemptItemHash,
|
|
4410
|
-
detail: `${attemptLabel}
|
|
4654
|
+
detail: `${attemptLabel} was discarded: ${lastError.message}`,
|
|
4411
4655
|
error: lastError.message
|
|
4412
4656
|
});
|
|
4413
4657
|
continue;
|
|
@@ -4439,6 +4683,17 @@ var SponsorRelayController = class {
|
|
|
4439
4683
|
this.patch({ errorText: "Connect MetaMask before deleting instances." });
|
|
4440
4684
|
return;
|
|
4441
4685
|
}
|
|
4686
|
+
const linkedRegistrationHashes = Array.from(
|
|
4687
|
+
new Set(
|
|
4688
|
+
this.state.bootstrapRegistrations.filter((entry) => entry.instanceItemHash === instanceHash).map(
|
|
4689
|
+
(entry) => entry.messageHash?.trim() ?? entry.itemHash?.trim() ?? entry.hash?.trim() ?? null
|
|
4690
|
+
).filter((value) => Boolean(value))
|
|
4691
|
+
)
|
|
4692
|
+
);
|
|
4693
|
+
const hashesToForget = Array.from(
|
|
4694
|
+
/* @__PURE__ */ new Set([instanceHash, ...linkedRegistrationHashes])
|
|
4695
|
+
);
|
|
4696
|
+
const linkedRegistrationCount = linkedRegistrationHashes.length;
|
|
4442
4697
|
this.patch({
|
|
4443
4698
|
busy: { deletingInstanceHash: instanceHash },
|
|
4444
4699
|
errorText: null,
|
|
@@ -4451,12 +4706,12 @@ var SponsorRelayController = class {
|
|
|
4451
4706
|
progress: 6,
|
|
4452
4707
|
status: "warning",
|
|
4453
4708
|
itemHash: instanceHash,
|
|
4454
|
-
detail: "Preparing Aleph FORGET message."
|
|
4709
|
+
detail: linkedRegistrationCount > 0 ? `Preparing Aleph FORGET message for the instance and ${linkedRegistrationCount} linked bootstrap registration${linkedRegistrationCount === 1 ? "" : "s"}.` : "Preparing Aleph FORGET message."
|
|
4455
4710
|
});
|
|
4456
4711
|
await forgetAlephMessages({
|
|
4457
4712
|
sender: this.state.wallet.address,
|
|
4458
|
-
hashes:
|
|
4459
|
-
reason: "Deleted from Sponsor Relay panel",
|
|
4713
|
+
hashes: hashesToForget,
|
|
4714
|
+
reason: linkedRegistrationCount > 0 ? `Deleted instance and ${linkedRegistrationCount} linked relay bootstrap registration${linkedRegistrationCount === 1 ? "" : "s"} from Sponsor Relay panel` : "Deleted from Sponsor Relay panel",
|
|
4460
4715
|
signer: personalSign,
|
|
4461
4716
|
hasher: sha256Hex,
|
|
4462
4717
|
fetch: asJsonFetch,
|
|
@@ -4475,17 +4730,20 @@ var SponsorRelayController = class {
|
|
|
4475
4730
|
progress: 92,
|
|
4476
4731
|
status: "info",
|
|
4477
4732
|
itemHash: instanceHash,
|
|
4478
|
-
detail: "Reloading current deployments."
|
|
4733
|
+
detail: linkedRegistrationCount > 0 ? "Reloading current deployments and relay bootstrap registrations." : "Reloading current deployments."
|
|
4479
4734
|
});
|
|
4480
4735
|
await this.refresh();
|
|
4481
|
-
this.trace("delete:submitted", {
|
|
4736
|
+
this.trace("delete:submitted", {
|
|
4737
|
+
instanceHash,
|
|
4738
|
+
linkedRegistrationHashes
|
|
4739
|
+
});
|
|
4482
4740
|
this.emitProgress({
|
|
4483
4741
|
stage: "completed",
|
|
4484
4742
|
label: "Delete completed",
|
|
4485
4743
|
progress: 100,
|
|
4486
4744
|
status: "success",
|
|
4487
4745
|
itemHash: instanceHash,
|
|
4488
|
-
detail: "Delete request submitted and deployments refreshed.",
|
|
4746
|
+
detail: linkedRegistrationCount > 0 ? `Delete request submitted, including ${linkedRegistrationCount} linked bootstrap registration${linkedRegistrationCount === 1 ? "" : "s"}, and state refreshed.` : "Delete request submitted and deployments refreshed.",
|
|
4489
4747
|
error: null
|
|
4490
4748
|
});
|
|
4491
4749
|
} catch (error) {
|
|
@@ -4589,10 +4847,9 @@ function useSponsorRelayController(props) {
|
|
|
4589
4847
|
);
|
|
4590
4848
|
useEffect(() => {
|
|
4591
4849
|
controller.updateProps({
|
|
4592
|
-
libp2p: props.libp2p,
|
|
4593
4850
|
debug: props.debug
|
|
4594
4851
|
});
|
|
4595
|
-
}, [controller, props.debug
|
|
4852
|
+
}, [controller, props.debug]);
|
|
4596
4853
|
useEffect(() => {
|
|
4597
4854
|
void controller.init();
|
|
4598
4855
|
return () => {
|
|
@@ -4633,6 +4890,53 @@ var fieldStyle = {
|
|
|
4633
4890
|
fontSize: "0.98rem",
|
|
4634
4891
|
lineHeight: 1.45
|
|
4635
4892
|
};
|
|
4893
|
+
var secondaryButtonStyle = {
|
|
4894
|
+
borderRadius: "0.95rem",
|
|
4895
|
+
border: "1px solid rgba(255,255,255,0.22)",
|
|
4896
|
+
background: "rgba(255,255,255,0.10)",
|
|
4897
|
+
color: "#f8fafc",
|
|
4898
|
+
padding: "0.72rem 0.95rem",
|
|
4899
|
+
fontFamily: '"DM Sans", "Inter", sans-serif',
|
|
4900
|
+
fontSize: "0.92rem",
|
|
4901
|
+
fontWeight: 700,
|
|
4902
|
+
lineHeight: 1.1,
|
|
4903
|
+
boxShadow: "inset 0 1px 0 rgba(255,255,255,0.06)",
|
|
4904
|
+
backdropFilter: "blur(8px)"
|
|
4905
|
+
};
|
|
4906
|
+
var primaryButtonStyle = {
|
|
4907
|
+
borderRadius: "0.95rem",
|
|
4908
|
+
border: "1px solid rgba(251, 191, 36, 0.42)",
|
|
4909
|
+
background: "linear-gradient(135deg, #f6c453 0%, #f59e0b 100%)",
|
|
4910
|
+
color: "#281606",
|
|
4911
|
+
padding: "0.82rem 1rem",
|
|
4912
|
+
fontFamily: '"DM Sans", "Inter", sans-serif',
|
|
4913
|
+
fontSize: "0.96rem",
|
|
4914
|
+
fontWeight: 800,
|
|
4915
|
+
lineHeight: 1.1,
|
|
4916
|
+
boxShadow: "0 10px 24px rgba(245, 158, 11, 0.24)"
|
|
4917
|
+
};
|
|
4918
|
+
var dangerButtonStyle = {
|
|
4919
|
+
borderRadius: "0.95rem",
|
|
4920
|
+
border: "1px solid rgba(248, 113, 113, 0.45)",
|
|
4921
|
+
background: "rgba(239, 68, 68, 0.18)",
|
|
4922
|
+
color: "#ffe2e2",
|
|
4923
|
+
padding: "0.72rem 0.95rem",
|
|
4924
|
+
fontFamily: '"DM Sans", "Inter", sans-serif',
|
|
4925
|
+
fontSize: "0.9rem",
|
|
4926
|
+
fontWeight: 700,
|
|
4927
|
+
lineHeight: 1.1
|
|
4928
|
+
};
|
|
4929
|
+
var warningButtonStyle = {
|
|
4930
|
+
borderRadius: "0.95rem",
|
|
4931
|
+
border: "1px solid rgba(251, 146, 60, 0.4)",
|
|
4932
|
+
background: "rgba(251, 146, 60, 0.16)",
|
|
4933
|
+
color: "#fed7aa",
|
|
4934
|
+
padding: "0.72rem 0.95rem",
|
|
4935
|
+
fontFamily: '"DM Sans", "Inter", sans-serif',
|
|
4936
|
+
fontSize: "0.9rem",
|
|
4937
|
+
fontWeight: 700,
|
|
4938
|
+
lineHeight: 1.1
|
|
4939
|
+
};
|
|
4636
4940
|
var themedLauncherBackground = "linear-gradient(135deg, var(--le-space-sponsor-relay-launcher-start, #4f46e5) 0%, var(--le-space-sponsor-relay-launcher-end, #6366f1) 100%)";
|
|
4637
4941
|
var themedLauncherBorder = "var(--le-space-sponsor-relay-launcher-border, rgba(199, 210, 254, 0.42))";
|
|
4638
4942
|
var themedLauncherBadgeBackground = "var(--le-space-sponsor-relay-launcher-badge-bg, rgba(49, 46, 129, 0.92))";
|
|
@@ -5038,7 +5342,15 @@ function SponsorRelayFab(props) {
|
|
|
5038
5342
|
}
|
|
5039
5343
|
)
|
|
5040
5344
|
] }),
|
|
5041
|
-
/* @__PURE__ */ jsx(
|
|
5345
|
+
/* @__PURE__ */ jsx(
|
|
5346
|
+
"button",
|
|
5347
|
+
{
|
|
5348
|
+
type: "button",
|
|
5349
|
+
style: secondaryButtonStyle,
|
|
5350
|
+
onClick: () => void controller.refresh(),
|
|
5351
|
+
children: state.busy.refreshing ? "Syncing" : "Refresh"
|
|
5352
|
+
}
|
|
5353
|
+
)
|
|
5042
5354
|
]
|
|
5043
5355
|
}
|
|
5044
5356
|
),
|
|
@@ -5397,7 +5709,7 @@ function SponsorRelayFab(props) {
|
|
|
5397
5709
|
{
|
|
5398
5710
|
type: "button",
|
|
5399
5711
|
onClick: () => void (state.wallet.connected ? controller.deploy() : controller.connectWallet()),
|
|
5400
|
-
style: { width: "100%", marginTop: "1rem" },
|
|
5712
|
+
style: { ...primaryButtonStyle, width: "100%", marginTop: "1rem" },
|
|
5401
5713
|
children: state.wallet.connected ? state.busy.deploying ? "Deploying\u2026" : "Deploy Relay" : "Connect MetaMask"
|
|
5402
5714
|
}
|
|
5403
5715
|
),
|
|
@@ -5454,7 +5766,7 @@ function SponsorRelayFab(props) {
|
|
|
5454
5766
|
}
|
|
5455
5767
|
}
|
|
5456
5768
|
),
|
|
5457
|
-
"
|
|
5769
|
+
"Aleph bootstrap registered"
|
|
5458
5770
|
]
|
|
5459
5771
|
}
|
|
5460
5772
|
) : null
|
|
@@ -5514,6 +5826,7 @@ function SponsorRelayFab(props) {
|
|
|
5514
5826
|
"button",
|
|
5515
5827
|
{
|
|
5516
5828
|
type: "button",
|
|
5829
|
+
style: dangerButtonStyle,
|
|
5517
5830
|
onClick: () => void controller.deleteInstance(
|
|
5518
5831
|
entry.instance.item_hash
|
|
5519
5832
|
),
|
|
@@ -5579,6 +5892,7 @@ function SponsorRelayFab(props) {
|
|
|
5579
5892
|
"button",
|
|
5580
5893
|
{
|
|
5581
5894
|
type: "button",
|
|
5895
|
+
style: warningButtonStyle,
|
|
5582
5896
|
disabled: !registrationHash || state.busy.deletingRegistrationHash === registrationHash,
|
|
5583
5897
|
onClick: () => registrationHash ? void controller.deleteBootstrapRegistration(
|
|
5584
5898
|
registrationHash
|