@le-space/ui 0.3.32 → 0.4.1

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
@@ -27,7 +27,7 @@ var RECENT_INSTANCE_RUNTIME_GRACE_MS = 10 * 60 * 1e3;
27
27
  var DEPLOYMENT_PENDING_WARNING_MS = 3 * 60 * 1e3;
28
28
 
29
29
  // src/shared/package-version.ts
30
- var UI_PACKAGE_VERSION = "0.3.32";
30
+ var UI_PACKAGE_VERSION = "0.4.1";
31
31
 
32
32
  // src/shared/format.ts
33
33
  function shortHash(value, head = 8, tail = 6) {
@@ -1236,6 +1236,16 @@ function normalizeBroadcastStatus(httpStatus, responseStatus) {
1236
1236
  if (httpStatus === 202) return "pending";
1237
1237
  return normalizeMessageStatus2(responseStatus);
1238
1238
  }
1239
+ function isInvalidMessageFormatResponse2(response, payload) {
1240
+ if (response.status !== 422) return false;
1241
+ const details = payload?.details;
1242
+ if (typeof details === "string" && details.includes("InvalidMessageFormat")) return true;
1243
+ if (details && typeof details === "object") {
1244
+ const detailMessage = details.message;
1245
+ if (typeof detailMessage === "string" && detailMessage.includes("InvalidMessageFormat")) return true;
1246
+ }
1247
+ return false;
1248
+ }
1239
1249
  function isRetryableBroadcastFailure(response, payload) {
1240
1250
  if (response.status >= 500) return true;
1241
1251
  const publicationStatus = payload?.publication_status?.status;
@@ -1257,22 +1267,25 @@ async function postBroadcastPayload2(body, options) {
1257
1267
  };
1258
1268
  }
1259
1269
  async function broadcastAlephMessage2(message, options) {
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, {
1270
+ const attempts = [
1271
+ { sync: options.sync ?? false, message },
1272
+ { ...message, sync: options.sync ?? false },
1273
+ { ...message }
1274
+ ];
1275
+ for (let index = 0; index < attempts.length; index += 1) {
1276
+ const result = await postBroadcastPayload2(attempts[index], {
1264
1277
  apiHost: options.apiHost,
1265
1278
  fetch: options.fetch
1266
1279
  });
1267
1280
  if (result.httpStatus === 202 || normalizeBroadcastStatus(result.httpStatus, result.response?.message_status) !== "unknown" || result.httpStatus >= 200 && result.httpStatus < 300) {
1268
1281
  return result;
1269
1282
  }
1270
- const canRetry = index < maxAttempts - 1 && isRetryableBroadcastFailure({ status: result.httpStatus }, result.response);
1283
+ const canRetry = index < attempts.length - 1 && (isInvalidMessageFormatResponse2({ status: result.httpStatus }, result.response) || isRetryableBroadcastFailure({ status: result.httpStatus }, result.response));
1271
1284
  if (!canRetry) {
1272
1285
  throw new Error(`Broadcast failed: ${result.httpStatus} ${JSON.stringify(result.response ?? {})}`);
1273
1286
  }
1274
1287
  }
1275
- throw new Error("Broadcast failed: retry budget exhausted");
1288
+ throw new Error("Broadcast failed: no compatible request format was accepted");
1276
1289
  }
1277
1290
 
1278
1291
  // ../core/src/forget.ts
@@ -1877,13 +1890,6 @@ async function waitForVmRuntime(args) {
1877
1890
  }
1878
1891
 
1879
1892
  // ../core/src/guest.ts
1880
- function proxyHostnameFromUrl(value) {
1881
- try {
1882
- return value ? new URL(value).hostname : null;
1883
- } catch {
1884
- return null;
1885
- }
1886
- }
1887
1893
  async function defaultHttpProbe(fetch2, url, timeoutMs = 1e4) {
1888
1894
  try {
1889
1895
  const controller = new AbortController();
@@ -2125,59 +2131,6 @@ async function fetchUcGoPeerMetadata(args) {
2125
2131
  `Relay metadata did not become ready after ${attempts} attempts: ${lastError ? lastError : typeof lastPayload === "string" ? lastPayload : JSON.stringify(lastPayload ?? {})}`
2126
2132
  );
2127
2133
  }
2128
- async function verifyUcGoPeerReachability(args) {
2129
- const checks = {};
2130
- const mappedPorts2 = args.mappedPorts ?? {};
2131
- const hostIpv4 = args.hostIpv4;
2132
- const skippedInternalPorts = new Set(
2133
- (args.skipInternalPorts ?? ["80"]).map((value) => String(value))
2134
- );
2135
- const httpProbe = args.httpProbe ?? ((url, timeoutMs) => defaultHttpProbe(args.fetch, url, timeoutMs));
2136
- if (hostIpv4) {
2137
- for (const [internalPort, mapping] of Object.entries(mappedPorts2)) {
2138
- if (skippedInternalPorts.has(String(internalPort))) continue;
2139
- if (mapping?.tcp === true && mapping?.host) {
2140
- checks[`tcp:${internalPort}`] = {
2141
- host: hostIpv4,
2142
- port: mapping.host,
2143
- ...await args.tcpProbe(
2144
- hostIpv4,
2145
- mapping.host,
2146
- Number(args.tcpTimeoutMs ?? 5e3)
2147
- )
2148
- };
2149
- } else if (mapping?.udp === true && mapping?.host) {
2150
- checks[`udp:${internalPort}`] = {
2151
- host: hostIpv4,
2152
- port: mapping.host,
2153
- ok: null,
2154
- note: "UDP mapping published; CI does not perform an application-level UDP handshake probe."
2155
- };
2156
- }
2157
- }
2158
- }
2159
- if (args.proxyUrl && args.verifyProxyHttp !== false) {
2160
- checks["https:proxy"] = await httpProbe(
2161
- args.proxyUrl,
2162
- Number(args.httpTimeoutMs ?? 1e4)
2163
- );
2164
- }
2165
- const proxyHostname = proxyHostnameFromUrl(args.proxyUrl);
2166
- if (proxyHostname) {
2167
- checks["tcp:proxy-443"] = await args.tcpProbe(
2168
- proxyHostname,
2169
- 443,
2170
- Number(args.tcpTimeoutMs ?? 5e3)
2171
- );
2172
- }
2173
- const failedChecks = Object.entries(checks).filter(
2174
- ([, value]) => value?.ok === false
2175
- );
2176
- return {
2177
- ok: failedChecks.length === 0,
2178
- checks
2179
- };
2180
- }
2181
2134
 
2182
2135
  // ../aleph-bootstrap/src/index.ts
2183
2136
  import { recoverMessageAddress } from "viem";
@@ -2597,7 +2550,7 @@ async function waitForRelayBootstrapRegistration(args) {
2597
2550
  return false;
2598
2551
  });
2599
2552
  if (match?.content) {
2600
- return {
2553
+ const visibleRegistration = {
2601
2554
  itemHash: match.itemHash ?? null,
2602
2555
  hash: match.hash ?? null,
2603
2556
  sender: typeof match.address === "string" ? match.address : null,
@@ -2606,7 +2559,10 @@ async function waitForRelayBootstrapRegistration(args) {
2606
2559
  multiaddrs: Array.isArray(match.content.multiaddrs) ? match.content.multiaddrs : [],
2607
2560
  browserMultiaddrs: Array.isArray(match.content.browserMultiaddrs) ? match.content.browserMultiaddrs : []
2608
2561
  };
2562
+ args.onAttempt?.(visibleRegistration, attempt + 1, attempts);
2563
+ return visibleRegistration;
2609
2564
  }
2565
+ args.onAttempt?.(null, attempt + 1, attempts);
2610
2566
  if (attempt < attempts - 1) {
2611
2567
  await sleep(delayMs);
2612
2568
  }
@@ -2719,16 +2675,6 @@ async function publishRelayBootstrapRegistration(args) {
2719
2675
 
2720
2676
  // ../core/src/bootstrap-config.ts
2721
2677
  var VM_BOOTSTRAP_CONFIG_AGGREGATE_KEY = "vm-bootstrap-config";
2722
- var VM_BOOTSTRAP_CONFIG_SIGNAL_REF = "vm-bootstrap-config";
2723
- var VM_BOOTSTRAP_CONFIG_SIGNAL_POST_TYPE = "vm-bootstrap-config-status";
2724
- function parseJsonObject(value) {
2725
- try {
2726
- const parsed = JSON.parse(value);
2727
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
2728
- } catch {
2729
- return null;
2730
- }
2731
- }
2732
2678
  async function fetchVmBootstrapConfigAggregate(address, options) {
2733
2679
  const requestUrl = new URL(`/api/v0/aggregates/${address}.json`, options.apiHost ?? DEFAULT_ALEPH_API_HOST2);
2734
2680
  requestUrl.searchParams.set("keys", VM_BOOTSTRAP_CONFIG_AGGREGATE_KEY);
@@ -2848,89 +2794,6 @@ async function deleteVmBootstrapConfig(args) {
2848
2794
  aggregate: content
2849
2795
  };
2850
2796
  }
2851
- function normalizeVmBootstrapConfigSignalRecord(value) {
2852
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2853
- const candidate = value;
2854
- const deploymentToken = typeof candidate.deploymentToken === "string" ? candidate.deploymentToken.trim() : "";
2855
- const status = typeof candidate.status === "string" ? candidate.status.trim() : "";
2856
- const profile = typeof candidate.profile === "string" ? candidate.profile.trim() : "";
2857
- const ownerAddress = typeof candidate.ownerAddress === "string" ? candidate.ownerAddress.trim() : "";
2858
- const instanceItemHash = typeof candidate.instanceItemHash === "string" ? candidate.instanceItemHash.trim() : "";
2859
- const updatedAt = typeof candidate.updatedAt === "string" ? candidate.updatedAt.trim() : "";
2860
- if (!deploymentToken || !status || !profile || !ownerAddress || !instanceItemHash || !updatedAt) {
2861
- return null;
2862
- }
2863
- return {
2864
- deploymentToken,
2865
- status,
2866
- profile,
2867
- ownerAddress,
2868
- instanceItemHash,
2869
- updatedAt,
2870
- publisherAddress: typeof candidate.publisherAddress === "string" && candidate.publisherAddress.trim() ? candidate.publisherAddress.trim() : null,
2871
- authorization: candidate.authorization,
2872
- peerId: typeof candidate.peerId === "string" && candidate.peerId.trim() ? candidate.peerId.trim() : null,
2873
- probeMultiaddrs: Array.isArray(candidate.probeMultiaddrs) ? candidate.probeMultiaddrs.filter(
2874
- (entry) => typeof entry === "string" && entry.trim().length > 0
2875
- ) : [],
2876
- browserBootstrapMultiaddrs: Array.isArray(candidate.browserBootstrapMultiaddrs) ? candidate.browserBootstrapMultiaddrs.filter(
2877
- (entry) => typeof entry === "string" && entry.trim().length > 0
2878
- ) : []
2879
- };
2880
- }
2881
- async function fetchVmBootstrapConfigSignals(args) {
2882
- const requestUrl = new URL("/api/v0/posts.json", args.apiHost ?? DEFAULT_ALEPH_API_HOST2);
2883
- requestUrl.searchParams.set("refs", args.ref ?? VM_BOOTSTRAP_CONFIG_SIGNAL_REF);
2884
- requestUrl.searchParams.set("types", args.postType ?? VM_BOOTSTRAP_CONFIG_SIGNAL_POST_TYPE);
2885
- requestUrl.searchParams.set("pagination", String(Math.max(1, Number(args.pagination ?? 100))));
2886
- requestUrl.searchParams.set("page", String(Math.max(1, Number(args.page ?? 1))));
2887
- requestUrl.searchParams.set("sortOrder", "-1");
2888
- const response = await args.fetch(requestUrl.toString(), { cache: "no-cache" });
2889
- if (!response.ok) {
2890
- throw new Error(`VM bootstrap config signal request failed: ${response.status}`);
2891
- }
2892
- const payload = await response.json();
2893
- const expectedDeploymentToken = args.deploymentToken?.trim() ?? "";
2894
- const expectedOwnerAddress = args.ownerAddress?.trim().toLowerCase() ?? "";
2895
- const expectedInstanceItemHash = args.instanceItemHash?.trim() ?? "";
2896
- return (payload.posts ?? []).map((entry) => {
2897
- if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
2898
- 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;
2899
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
2900
- const content = "deploymentToken" in parsed ? parsed : "content" in parsed && parsed.content && typeof parsed.content === "object" && !Array.isArray(parsed.content) ? parsed.content : null;
2901
- if (!content || typeof content !== "object" || Array.isArray(content)) return null;
2902
- return normalizeVmBootstrapConfigSignalRecord(content);
2903
- }).filter((entry) => {
2904
- if (!entry) return false;
2905
- if (expectedDeploymentToken && entry.deploymentToken !== expectedDeploymentToken) return false;
2906
- if (expectedOwnerAddress && entry.ownerAddress.toLowerCase() !== expectedOwnerAddress) return false;
2907
- if (expectedInstanceItemHash && entry.instanceItemHash !== expectedInstanceItemHash) return false;
2908
- return true;
2909
- });
2910
- }
2911
- async function waitForVmBootstrapConfigSignal(args) {
2912
- const attempts = Math.max(1, Number(args.attempts ?? 24));
2913
- const delayMs = Math.max(0, Number(args.delayMs ?? 2500));
2914
- const sleep2 = args.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
2915
- const expectedStatus = args.expectedStatus ?? "applied";
2916
- for (let attempt = 0; attempt < attempts; attempt += 1) {
2917
- const signals = await fetchVmBootstrapConfigSignals({
2918
- deploymentToken: args.deploymentToken,
2919
- ownerAddress: args.ownerAddress,
2920
- instanceItemHash: args.instanceItemHash,
2921
- fetch: args.fetch,
2922
- apiHost: args.apiHost,
2923
- ref: args.ref,
2924
- postType: args.postType
2925
- }).catch(() => []);
2926
- const match = signals.find((entry) => entry.status === expectedStatus) ?? null;
2927
- if (match) return match;
2928
- if (attempt < attempts - 1) {
2929
- await sleep2(delayMs);
2930
- }
2931
- }
2932
- return null;
2933
- }
2934
2797
 
2935
2798
  // ../core/src/bootstrap-reconcile.ts
2936
2799
  function createRelayBootstrapRegistrationId(profile, instanceName, itemHash) {
@@ -3302,8 +3165,6 @@ var UI_DEPLOY_WAIT_ATTEMPTS = 60;
3302
3165
  var UI_DEPLOY_WAIT_DELAY_MS = 5e3;
3303
3166
  var UI_RUNTIME_WAIT_ATTEMPTS = 40;
3304
3167
  var UI_RUNTIME_WAIT_DELAY_MS = 5e3;
3305
- var UI_REACHABILITY_VERIFY_ATTEMPTS = 25;
3306
- var UI_REACHABILITY_VERIFY_DELAY_MS = 3e3;
3307
3168
  function hasUsableRuntime(details) {
3308
3169
  return Boolean(details.hostIpv4) || Boolean(details.vmIpv4) || details.mappedPorts.length > 0 || Boolean(details.webUrl) || Boolean(details.execution);
3309
3170
  }
@@ -3410,21 +3271,19 @@ var SponsorRelayController = class {
3410
3271
  this.progressEmitter.emit(nextEvent);
3411
3272
  }
3412
3273
  async configureRelayBootstrapRegistration(args) {
3413
- let runtime = args.runtime;
3414
3274
  const profile = relayProfileForManifest(this.state.manifest);
3415
3275
  if (!profile) return;
3416
3276
  if (!this.state.wallet.address) {
3417
3277
  throw new Error("A connected wallet is required to register relay bootstrap addresses.");
3418
3278
  }
3419
- if (!runtime.hostIpv4) {
3279
+ if (!args.runtime.hostIpv4) {
3420
3280
  throw new Error("Relay runtime did not expose a host IPv4 address.");
3421
3281
  }
3422
- let mappedPorts2 = runtime.mappedPorts ?? {};
3282
+ const mappedPorts2 = args.runtime.mappedPorts ?? {};
3423
3283
  const setupPort = mappedPorts2["80"]?.host ?? null;
3424
3284
  if (!setupPort) {
3425
3285
  throw new Error("Relay runtime did not expose the temporary setup endpoint.");
3426
3286
  }
3427
- let bootstrapSignalRelayMetadata = null;
3428
3287
  if (profile === "orbitdb-relay-pinner") {
3429
3288
  this.emitProgress({
3430
3289
  stage: "deployment-confirmed",
@@ -3435,12 +3294,22 @@ var SponsorRelayController = class {
3435
3294
  detail: "Waiting for the guest setup endpoint before collecting relay metadata."
3436
3295
  });
3437
3296
  const setupHealth = await waitForSetupEndpoint({
3438
- hostIpv4: runtime.hostIpv4,
3297
+ hostIpv4: args.runtime.hostIpv4,
3439
3298
  setupPort,
3440
3299
  fetch: (url, init) => fetch(url, init),
3441
3300
  attempts: 15,
3442
3301
  delayMs: 4e3,
3443
- httpTimeoutMs: 1e4
3302
+ httpTimeoutMs: 1e4,
3303
+ onAttempt: (result, attempt, attempts) => {
3304
+ this.emitProgress({
3305
+ stage: "deployment-confirmed",
3306
+ label: "Waiting for guest setup",
3307
+ progress: 93,
3308
+ status: result.ok ? "success" : "info",
3309
+ itemHash: args.itemHash,
3310
+ 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.`
3311
+ });
3312
+ }
3444
3313
  });
3445
3314
  if (!setupHealth.ok) {
3446
3315
  throw new Error(
@@ -3461,12 +3330,12 @@ var SponsorRelayController = class {
3461
3330
  throw new Error("OrbitDB relay runtime is missing required mapped TCP/WS ports.");
3462
3331
  }
3463
3332
  const configureResult = await configureOrbitdbRelaySetup2({
3464
- hostIpv4: runtime.hostIpv4,
3465
- publicIpv6: runtime.ipv6,
3333
+ hostIpv4: args.runtime.hostIpv4,
3334
+ publicIpv6: args.runtime.ipv6,
3466
3335
  setupPort,
3467
3336
  tcpPort,
3468
3337
  wsPort,
3469
- proxyUrl: runtime.proxyUrl ?? void 0,
3338
+ proxyUrl: args.runtime.proxyUrl ?? void 0,
3470
3339
  metricsPort: mappedPorts2["9090"]?.host ?? null,
3471
3340
  metricsHttpsPort: mappedPorts2["443"]?.host ?? null,
3472
3341
  webrtcPort: mappedPorts2["9093"]?.host ?? null,
@@ -3483,54 +3352,6 @@ var SponsorRelayController = class {
3483
3352
  if (!args.deploymentToken) {
3484
3353
  throw new Error("Missing deployment token for Aleph bootstrap config handoff.");
3485
3354
  }
3486
- if (runtime.proxyUrl && runtime.webAccess?.active !== true) {
3487
- this.emitProgress({
3488
- stage: "deployment-confirmed",
3489
- label: "Waiting for HTTPS route",
3490
- progress: 93,
3491
- status: "warning",
3492
- itemHash: args.itemHash,
3493
- detail: "The relay received a 2n6 hostname, but it is not active yet. Waiting before handing HTTPS config to the guest."
3494
- });
3495
- let latestRuntime = runtime;
3496
- for (let attempt = 0; attempt < UI_RUNTIME_WAIT_ATTEMPTS; attempt += 1) {
3497
- if (latestRuntime.webAccess?.active === true) {
3498
- break;
3499
- }
3500
- await new Promise(
3501
- (resolve) => globalThis.setTimeout(resolve, UI_RUNTIME_WAIT_DELAY_MS)
3502
- );
3503
- latestRuntime = await fetchVmRuntime({
3504
- itemHash: args.itemHash,
3505
- fetch: (url, init) => fetch(url, init),
3506
- crnHash: runtime.selectedCrn?.hash ?? runtime.allocation?.crnHash ?? void 0,
3507
- crns: this.state.crns,
3508
- crnListUrl: this.props.crnListUrl
3509
- }).catch(() => latestRuntime);
3510
- this.emitProgress({
3511
- stage: "deployment-confirmed",
3512
- label: "Waiting for HTTPS route",
3513
- progress: 93,
3514
- status: latestRuntime.webAccess?.active === true ? "info" : "warning",
3515
- itemHash: args.itemHash,
3516
- detail: `2n6 activation ${attempt + 1}/${UI_RUNTIME_WAIT_ATTEMPTS}: ${latestRuntime.webAccess?.active === true ? "active" : "still pending"} for ${latestRuntime.proxyUrl ?? runtime.proxyUrl ?? "reserved proxy URL"}.`
3517
- });
3518
- this.trace("deploy:bootstrap-proxy-activation", {
3519
- itemHash: args.itemHash,
3520
- attempt: attempt + 1,
3521
- attempts: UI_RUNTIME_WAIT_ATTEMPTS,
3522
- active: latestRuntime.webAccess?.active === true,
3523
- proxyUrl: latestRuntime.proxyUrl ?? null
3524
- });
3525
- }
3526
- runtime = latestRuntime;
3527
- mappedPorts2 = runtime.mappedPorts ?? mappedPorts2;
3528
- }
3529
- if (!runtime.hostIpv4) {
3530
- throw new Error("Relay runtime lost its host IPv4 address while waiting for HTTPS activation.");
3531
- }
3532
- const runtimeHostIpv4 = runtime.hostIpv4;
3533
- const activeProxyUrl = runtime.webAccess?.active === true ? runtime.proxyUrl ?? null : null;
3534
3355
  this.emitProgress({
3535
3356
  stage: "deployment-confirmed",
3536
3357
  label: "Publishing relay config",
@@ -3548,9 +3369,9 @@ var SponsorRelayController = class {
3548
3369
  expiresAt: new Date(Date.now() + 30 * 6e4).toISOString(),
3549
3370
  status: "pending",
3550
3371
  runtime: {
3551
- publicIpv4: runtimeHostIpv4,
3552
- publicIpv6: runtime.ipv6 ?? null,
3553
- proxyUrl: activeProxyUrl,
3372
+ publicIpv4: args.runtime.hostIpv4,
3373
+ publicIpv6: args.runtime.ipv6 ?? null,
3374
+ proxyUrl: args.runtime.proxyUrl ?? null,
3554
3375
  mappedPorts: mappedPorts2
3555
3376
  },
3556
3377
  bootstrap: {
@@ -3579,164 +3400,57 @@ var SponsorRelayController = class {
3579
3400
  aggregateItemHash: bootstrapConfigPublication.aggregateItemHash,
3580
3401
  aggregateStatus: bootstrapConfigPublication.aggregateStatus
3581
3402
  });
3582
- this.emitProgress({
3583
- stage: "deployment-confirmed",
3584
- label: "Waiting for guest config",
3585
- progress: 94,
3586
- status: "info",
3587
- itemHash: args.itemHash,
3588
- detail: "Waiting for the relay VM to acknowledge that it consumed the Aleph bootstrap config."
3589
- });
3590
- const bootstrapConfigSignal = await waitForVmBootstrapConfigSignal({
3591
- deploymentToken: args.deploymentToken,
3592
- ownerAddress: this.state.wallet.address,
3593
- instanceItemHash: args.itemHash,
3594
- fetch: asJsonFetch,
3595
- apiHost: this.client.apiHost,
3596
- attempts: 40,
3597
- delayMs: 3e3
3598
- });
3599
- this.trace("deploy:bootstrap-config-signal-visible", {
3600
- itemHash: args.itemHash,
3601
- deploymentToken: args.deploymentToken,
3602
- bootstrapConfigSignal
3603
- });
3604
- if (!bootstrapConfigSignal) {
3605
- throw new Error(
3606
- "The relay VM did not confirm that it applied the Aleph bootstrap config in time."
3607
- );
3608
- }
3609
- bootstrapSignalRelayMetadata = typeof bootstrapConfigSignal.peerId === "string" && bootstrapConfigSignal.peerId.length > 0 && Array.isArray(bootstrapConfigSignal.probeMultiaddrs) && bootstrapConfigSignal.probeMultiaddrs.length > 0 ? {
3610
- peerId: bootstrapConfigSignal.peerId,
3611
- probeMultiaddrs: bootstrapConfigSignal.probeMultiaddrs,
3612
- browserBootstrapMultiaddrs: bootstrapConfigSignal.browserBootstrapMultiaddrs ?? []
3613
- } : null;
3614
- if (runtime.proxyUrl && runtime.webAccess?.active !== true) {
3615
- this.trace("deploy:bootstrap-proxy-fallback", {
3403
+ }
3404
+ this.emitProgress({
3405
+ stage: "publishing-bootstrap",
3406
+ label: "Reading relay multiaddrs",
3407
+ progress: 95,
3408
+ status: "info",
3409
+ itemHash: args.itemHash,
3410
+ detail: "Waiting for the relay to report its final public multiaddrs."
3411
+ });
3412
+ const relayMetadata = await fetchRelayMetadataForRuntime({
3413
+ runtime: args.runtime,
3414
+ fetch: (url, init) => fetch(url, init),
3415
+ preferSecureMetadata: profile === "uc-go-peer",
3416
+ attempts: 80,
3417
+ delayMs: 3e3,
3418
+ timeoutMs: 24e4,
3419
+ onAttempt: (result) => {
3420
+ const payload = result.payload && typeof result.payload === "object" ? result.payload : null;
3421
+ const metadata = payload?.metadata && typeof payload.metadata === "object" ? payload.metadata : null;
3422
+ this.trace("deploy:relay-metadata-attempt", {
3616
3423
  itemHash: args.itemHash,
3617
- deploymentToken: args.deploymentToken,
3618
- proxyUrl: runtime.proxyUrl,
3619
- bootstrapSignalRelayMetadata
3424
+ attempt: result.attempt,
3425
+ attempts: result.attempts,
3426
+ requestUrl: result.requestUrl,
3427
+ metadataUrl: result.metadataUrl,
3428
+ hostIpv4: result.hostIpv4,
3429
+ setupPort: result.setupPort,
3430
+ ok: result.ok,
3431
+ status: result.status,
3432
+ ready: result.ready,
3433
+ error: result.error ?? null,
3434
+ payloadStatus: payload && typeof payload.status === "string" ? payload.status : null,
3435
+ peerId: metadata && typeof metadata.peer_id === "string" ? metadata.peer_id : null,
3436
+ probeMultiaddrCount: Array.isArray(metadata?.probe_multiaddrs) ? metadata.probe_multiaddrs.length : 0,
3437
+ browserBootstrapMultiaddrCount: Array.isArray(
3438
+ metadata?.browser_bootstrap_multiaddrs
3439
+ ) ? metadata.browser_bootstrap_multiaddrs.length : 0
3620
3440
  });
3621
3441
  this.emitProgress({
3622
- stage: "deployment-confirmed",
3623
- label: "Continuing without HTTPS route",
3624
- progress: 94,
3625
- status: "warning",
3442
+ stage: "publishing-bootstrap",
3443
+ label: "Reading relay multiaddrs",
3444
+ progress: 95,
3445
+ status: result.ready ? "success" : "info",
3626
3446
  itemHash: args.itemHash,
3627
- 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."
3447
+ detail: result.ready ? `Relay metadata ${result.attempt}/${result.attempts}: public multiaddrs available.` : `Relay metadata ${result.attempt}/${result.attempts}: waiting for the relay to report public multiaddrs.`
3628
3448
  });
3629
- runtime = {
3630
- ...runtime,
3631
- proxyUrl: null,
3632
- webAccess: runtime.webAccess ? {
3633
- ...runtime.webAccess,
3634
- active: false
3635
- } : runtime.webAccess
3636
- };
3637
3449
  }
3638
- }
3639
- let relayMetadata = bootstrapSignalRelayMetadata;
3640
- if (relayMetadata) {
3641
- this.trace("deploy:relay-metadata-from-bootstrap-signal", {
3642
- itemHash: args.itemHash,
3643
- peerId: relayMetadata.peerId,
3644
- probeMultiaddrCount: relayMetadata.probeMultiaddrs.length,
3645
- browserBootstrapMultiaddrCount: relayMetadata.browserBootstrapMultiaddrs.length
3646
- });
3647
- } else {
3648
- this.emitProgress({
3649
- stage: "publishing-bootstrap",
3650
- label: "Reading relay multiaddrs",
3651
- progress: 95,
3652
- status: "info",
3653
- itemHash: args.itemHash,
3654
- detail: "Waiting for the relay to report its final public multiaddrs."
3655
- });
3656
- relayMetadata = await fetchRelayMetadataForRuntime({
3657
- runtime,
3658
- fetch: (url, init) => fetch(url, init),
3659
- preferSecureMetadata: profile === "uc-go-peer" && Boolean(runtime.proxyUrl),
3660
- attempts: 80,
3661
- delayMs: 3e3,
3662
- timeoutMs: 24e4,
3663
- onAttempt: (result) => {
3664
- const payload = result.payload && typeof result.payload === "object" ? result.payload : null;
3665
- const metadata = payload?.metadata && typeof payload.metadata === "object" ? payload.metadata : null;
3666
- this.trace("deploy:relay-metadata-attempt", {
3667
- itemHash: args.itemHash,
3668
- attempt: result.attempt,
3669
- attempts: result.attempts,
3670
- requestUrl: result.requestUrl,
3671
- metadataUrl: result.metadataUrl,
3672
- hostIpv4: result.hostIpv4,
3673
- setupPort: result.setupPort,
3674
- ok: result.ok,
3675
- status: result.status,
3676
- ready: result.ready,
3677
- error: result.error ?? null,
3678
- payloadStatus: payload && typeof payload.status === "string" ? payload.status : null,
3679
- peerId: metadata && typeof metadata.peer_id === "string" ? metadata.peer_id : null,
3680
- probeMultiaddrCount: Array.isArray(metadata?.probe_multiaddrs) ? metadata.probe_multiaddrs.length : 0,
3681
- browserBootstrapMultiaddrCount: Array.isArray(
3682
- metadata?.browser_bootstrap_multiaddrs
3683
- ) ? metadata.browser_bootstrap_multiaddrs.length : 0
3684
- });
3685
- }
3686
- });
3687
- }
3450
+ });
3688
3451
  if (!relayMetadata) {
3689
3452
  throw new Error("Relay metadata did not include a peer ID and public multiaddrs.");
3690
3453
  }
3691
- if (profile === "uc-go-peer" && runtime.proxyUrl) {
3692
- this.emitProgress({
3693
- stage: "publishing-bootstrap",
3694
- label: "Verifying reachability",
3695
- progress: 96,
3696
- status: "info",
3697
- itemHash: args.itemHash,
3698
- detail: "Checking that the secure relay endpoint is reachable before trusting the deployment as ready."
3699
- });
3700
- const verificationUrl = (() => {
3701
- try {
3702
- return new URL("/bootstrap/metadata", runtime.proxyUrl).toString();
3703
- } catch {
3704
- return runtime.proxyUrl ?? "";
3705
- }
3706
- })();
3707
- let verificationResult = null;
3708
- for (let attempt = 0; attempt < UI_REACHABILITY_VERIFY_ATTEMPTS; attempt += 1) {
3709
- verificationResult = await verifyUcGoPeerReachability({
3710
- proxyUrl: verificationUrl,
3711
- verifyProxyHttp: true,
3712
- fetch: (url, init) => fetch(url, init),
3713
- tcpProbe: async () => ({ ok: null })
3714
- });
3715
- this.trace("deploy:reachability-verification-attempt", {
3716
- itemHash: args.itemHash,
3717
- attempt: attempt + 1,
3718
- attempts: UI_REACHABILITY_VERIFY_ATTEMPTS,
3719
- verificationUrl,
3720
- ok: verificationResult.ok,
3721
- checks: verificationResult.checks
3722
- });
3723
- const proxyHttpCheck2 = verificationResult.checks["https:proxy"];
3724
- if (verificationResult.ok && proxyHttpCheck2?.ok === true) {
3725
- break;
3726
- }
3727
- if (attempt < UI_REACHABILITY_VERIFY_ATTEMPTS - 1) {
3728
- await new Promise(
3729
- (resolve) => globalThis.setTimeout(resolve, UI_REACHABILITY_VERIFY_DELAY_MS)
3730
- );
3731
- }
3732
- }
3733
- const proxyHttpCheck = verificationResult?.checks["https:proxy"];
3734
- if (!verificationResult?.ok || proxyHttpCheck?.ok !== true) {
3735
- throw new Error(
3736
- proxyHttpCheck?.error ? `Secure relay endpoint did not become reachable in time: ${proxyHttpCheck.error}` : "Secure relay endpoint did not become reachable in time."
3737
- );
3738
- }
3739
- }
3740
3454
  const { peerId } = relayMetadata;
3741
3455
  const registrationId = createRelayBootstrapRegistrationId(
3742
3456
  profile,
@@ -3757,7 +3471,17 @@ var SponsorRelayController = class {
3757
3471
  fetch: asJsonFetch,
3758
3472
  apiHost: this.client.apiHost,
3759
3473
  attempts: 24,
3760
- delayMs: 2500
3474
+ delayMs: 2500,
3475
+ onAttempt: (match, attempt, attempts) => {
3476
+ this.emitProgress({
3477
+ stage: "publishing-bootstrap",
3478
+ label: "Waiting for guest bootstrap registration",
3479
+ progress: 97,
3480
+ status: match ? "success" : "info",
3481
+ itemHash: args.itemHash,
3482
+ detail: match ? `Guest bootstrap registration ${attempt}/${attempts}: visible on Aleph.` : `Guest bootstrap registration ${attempt}/${attempts}: still waiting for the relay VM registration on Aleph.`
3483
+ });
3484
+ }
3761
3485
  });
3762
3486
  this.trace("deploy:bootstrap-registration-visible", {
3763
3487
  itemHash: args.itemHash,
@@ -3808,7 +3532,17 @@ var SponsorRelayController = class {
3808
3532
  fetch: asJsonFetch,
3809
3533
  apiHost: this.client.apiHost,
3810
3534
  attempts: 24,
3811
- delayMs: 2500
3535
+ delayMs: 2500,
3536
+ onAttempt: (match, attempt, attempts) => {
3537
+ this.emitProgress({
3538
+ stage: "publishing-bootstrap",
3539
+ label: "Waiting for bootstrap fallback",
3540
+ progress: 98,
3541
+ status: match ? "success" : "warning",
3542
+ itemHash: args.itemHash,
3543
+ detail: match ? `Bootstrap fallback ${attempt}/${attempts}: visible on Aleph.` : `Bootstrap fallback ${attempt}/${attempts}: waiting for the browser-published registration on Aleph.`
3544
+ });
3545
+ }
3812
3546
  });
3813
3547
  this.trace("deploy:bootstrap-registration-fallback-visible", {
3814
3548
  itemHash: args.itemHash,
@@ -3854,7 +3588,6 @@ var SponsorRelayController = class {
3854
3588
  const currentProgress = this.state.deploymentProgress;
3855
3589
  const currentProgressIsForLatestHash = currentProgress.itemHash === itemHash || currentProgress.itemHash == null && currentProgress.stage !== "idle" && currentProgress.stage !== "error";
3856
3590
  const keepTerminalSuccess = this.lastCompletedDeploymentHash === itemHash || currentProgressIsForLatestHash && currentProgress.stage === "completed" && currentProgress.status === "success";
3857
- const keepSpecificInFlightProgress = currentProgressIsForLatestHash && currentProgress.stage !== "completed" && currentProgress.stage !== "error" && currentProgress.progress >= 93;
3858
3591
  const status = latest.details.messageStatus;
3859
3592
  if (status === "rejected") {
3860
3593
  this.emitProgress({
@@ -3897,16 +3630,6 @@ var SponsorRelayController = class {
3897
3630
  });
3898
3631
  return;
3899
3632
  }
3900
- if (keepSpecificInFlightProgress) {
3901
- this.trace("progress:retain-inflight", {
3902
- reason: "latest refresh would overwrite a more specific in-flight deployment phase",
3903
- itemHash,
3904
- currentStage: currentProgress.stage,
3905
- currentLabel: currentProgress.label,
3906
- currentProgress: currentProgress.progress
3907
- });
3908
- return;
3909
- }
3910
3633
  this.emitProgress({
3911
3634
  stage: "deployment-confirmed",
3912
3635
  label: "Waiting for runtime",
@@ -3917,16 +3640,6 @@ var SponsorRelayController = class {
3917
3640
  });
3918
3641
  return;
3919
3642
  }
3920
- if (keepSpecificInFlightProgress) {
3921
- this.trace("progress:retain-inflight", {
3922
- reason: "latest refresh would overwrite a more specific in-flight deployment phase",
3923
- itemHash,
3924
- currentStage: currentProgress.stage,
3925
- currentLabel: currentProgress.label,
3926
- currentProgress: currentProgress.progress
3927
- });
3928
- return;
3929
- }
3930
3643
  this.emitProgress({
3931
3644
  stage: "deployment-confirmed",
3932
3645
  label: "Runtime ready",
@@ -4420,7 +4133,7 @@ var SponsorRelayController = class {
4420
4133
  fetch: (url, init) => fetch(url, init),
4421
4134
  attempts: UI_DEPLOY_WAIT_ATTEMPTS,
4422
4135
  delayMs: UI_DEPLOY_WAIT_DELAY_MS,
4423
- onAttempt: (inspectionResult) => {
4136
+ onAttempt: (inspectionResult, attempt, attempts) => {
4424
4137
  if (inspectionResult.status === "processed") {
4425
4138
  this.emitProgress({
4426
4139
  stage: "deployment-confirmed",
@@ -4428,7 +4141,7 @@ var SponsorRelayController = class {
4428
4141
  progress: 82,
4429
4142
  status: "success",
4430
4143
  itemHash: result.itemHash,
4431
- detail: "Aleph processed the instance message."
4144
+ detail: `Aleph processing ${attempt}/${attempts}: instance message processed.`
4432
4145
  });
4433
4146
  return;
4434
4147
  }
@@ -4450,7 +4163,7 @@ var SponsorRelayController = class {
4450
4163
  progress: 76,
4451
4164
  status: "info",
4452
4165
  itemHash: result.itemHash,
4453
- detail: "Deployment submitted. Waiting for Aleph to process the instance message."
4166
+ detail: `Aleph processing ${attempt}/${attempts}: waiting for the instance message to be processed.`
4454
4167
  });
4455
4168
  }
4456
4169
  });
@@ -4516,7 +4229,7 @@ var SponsorRelayController = class {
4516
4229
  crnListUrl: this.props.crnListUrl,
4517
4230
  attempts: UI_RUNTIME_WAIT_ATTEMPTS,
4518
4231
  delayMs: UI_RUNTIME_WAIT_DELAY_MS,
4519
- onAttempt: (runtime2) => {
4232
+ onAttempt: (runtime2, attempt, attempts) => {
4520
4233
  if (runtime2.hostIpv4 && Object.keys(runtime2.mappedPorts ?? {}).length > 0) {
4521
4234
  this.emitProgress({
4522
4235
  stage: "deployment-confirmed",
@@ -4524,7 +4237,7 @@ var SponsorRelayController = class {
4524
4237
  progress: 92,
4525
4238
  status: "info",
4526
4239
  itemHash: result.itemHash,
4527
- detail: "Runtime networking and mapped ports are now available. Finishing relay setup and bootstrap verification."
4240
+ detail: `Runtime ${attempt}/${attempts}: networking and mapped ports are now available.`
4528
4241
  });
4529
4242
  return;
4530
4243
  }
@@ -4534,7 +4247,7 @@ var SponsorRelayController = class {
4534
4247
  progress: 90,
4535
4248
  status: "warning",
4536
4249
  itemHash: result.itemHash,
4537
- detail: runtime2.diagnostics?.reason ?? "Waiting for CRN runtime networking and mapped ports."
4250
+ detail: runtime2.diagnostics?.reason ? `Runtime ${attempt}/${attempts}: ${runtime2.diagnostics.reason}` : `Runtime ${attempt}/${attempts}: waiting for CRN runtime networking and mapped ports.`
4538
4251
  });
4539
4252
  }
4540
4253
  });
@@ -4596,15 +4309,6 @@ var SponsorRelayController = class {
4596
4309
  });
4597
4310
  if (attemptItemHash) {
4598
4311
  if (attemptDeploymentToken) {
4599
- this.emitProgress({
4600
- stage: "refreshing-instances",
4601
- label: "Cleaning up failed attempt",
4602
- progress: 16,
4603
- status: "warning",
4604
- itemHash: attemptItemHash,
4605
- detail: `Removing bootstrap handoff state for failed deployment on ${attemptLabel}.`,
4606
- error: lastError.message
4607
- });
4608
4312
  this.trace("deploy:bootstrap-config-cleanup-start", {
4609
4313
  itemHash: attemptItemHash,
4610
4314
  deploymentToken: attemptDeploymentToken
@@ -4630,26 +4334,8 @@ var SponsorRelayController = class {
4630
4334
  deploymentToken: attemptDeploymentToken,
4631
4335
  error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
4632
4336
  });
4633
- this.emitProgress({
4634
- stage: "refreshing-instances",
4635
- label: "Cleanup warning",
4636
- progress: 16,
4637
- status: "warning",
4638
- itemHash: attemptItemHash,
4639
- detail: `Bootstrap cleanup reported an issue for ${attemptLabel}, but retry cleanup will continue.`,
4640
- error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
4641
- });
4642
4337
  });
4643
4338
  }
4644
- this.emitProgress({
4645
- stage: "refreshing-instances",
4646
- label: "Discarding failed deployment",
4647
- progress: 17,
4648
- status: "warning",
4649
- itemHash: attemptItemHash,
4650
- detail: `Forgetting failed deployment ${attemptItemHash.slice(0, 12)} on ${attemptLabel} before retrying.`,
4651
- error: lastError.message
4652
- });
4653
4339
  this.trace("deploy:forget-failed-attempt-start", {
4654
4340
  itemHash: attemptItemHash,
4655
4341
  crnHash: candidateCrn.hash,
@@ -4672,30 +4358,12 @@ var SponsorRelayController = class {
4672
4358
  itemHash: attemptItemHash,
4673
4359
  error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
4674
4360
  });
4675
- this.emitProgress({
4676
- stage: "refreshing-instances",
4677
- label: "Cleanup warning",
4678
- progress: 17,
4679
- status: "warning",
4680
- itemHash: attemptItemHash,
4681
- detail: `Failed deployment cleanup reported an issue for ${attemptLabel}.`,
4682
- error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
4683
- });
4684
4361
  });
4685
4362
  this.trace("deploy:forget-failed-attempt-complete", {
4686
4363
  itemHash: attemptItemHash,
4687
4364
  crnHash: candidateCrn.hash,
4688
4365
  crnName: candidateCrn.name
4689
4366
  });
4690
- this.emitProgress({
4691
- stage: "refreshing-instances",
4692
- label: "Cleanup complete",
4693
- progress: 18,
4694
- status: "info",
4695
- itemHash: attemptItemHash,
4696
- detail: `Finished cleaning up failed deployment on ${attemptLabel}.`,
4697
- error: null
4698
- });
4699
4367
  }
4700
4368
  if (candidateIndex < orderedCrns.length - 1) {
4701
4369
  this.emitProgress({