@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/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.0";
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 attempts = [
1282
- { sync: options.sync ?? false, message },
1283
- { ...message, sync: options.sync ?? false },
1284
- { ...message }
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 < attempts.length - 1 && (isInvalidMessageFormatResponse2({ status: result.httpStatus }, result.response) || isRetryableBroadcastFailure({ status: result.httpStatus }, result.response));
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: no compatible request format was accepted");
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.hostIpv4 && Object.keys(lastRuntime.mappedPorts ?? {}).length > 0) {
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
- return {
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
- const mappedPorts2 = args.runtime.mappedPorts ?? {};
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: args.runtime.hostIpv4,
3425
- publicIpv6: args.runtime.ipv6 ?? null,
3426
- proxyUrl: args.runtime.proxyUrl ?? null,
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
- this.emitProgress({
3457
- stage: "publishing-bootstrap",
3458
- label: "Reading relay multiaddrs",
3459
- progress: 95,
3460
- status: "info",
3461
- itemHash: args.itemHash,
3462
- detail: "Waiting for the relay to report its final public multiaddrs."
3463
- });
3464
- const relayMetadata = await fetchRelayMetadataForRuntime({
3465
- runtime: args.runtime,
3466
- fetch: (url, init) => fetch(url, init),
3467
- preferSecureMetadata: profile === "uc-go-peer",
3468
- attempts: 80,
3469
- delayMs: 3e3,
3470
- timeoutMs: 24e4,
3471
- onAttempt: (result) => {
3472
- const payload = result.payload && typeof result.payload === "object" ? result.payload : null;
3473
- const metadata = payload?.metadata && typeof payload.metadata === "object" ? payload.metadata : null;
3474
- this.trace("deploy:relay-metadata-attempt", {
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
- attempt: result.attempt,
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: "Aleph processed the instance message."
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: "Deployment submitted. Waiting for Aleph to process the instance message."
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.hostIpv4 && Object.keys(runtime2.mappedPorts ?? {}).length > 0) {
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: "Runtime networking and mapped ports are now available. Finishing relay setup and bootstrap verification."
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 ?? "Waiting for CRN runtime networking and mapped ports."
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 (!runtime.hostIpv4 || Object.keys(runtime.mappedPorts ?? {}).length === 0) {
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: `Retrying after ${crnAttemptLabel}`,
4649
+ label: "Retrying on next CRN",
4406
4650
  progress: 20,
4407
4651
  status: "warning",
4408
4652
  itemHash: attemptItemHash,
4409
- detail: `${attemptLabel} failed: ${lastError.message}`,
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: [instanceHash],
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", { instanceHash });
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,