@le-space/ui 0.1.54 → 0.1.56
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 +9 -1
- package/react/index.d.ts +17 -0
- package/react/index.js +983 -124
- package/shared/index.d.ts +27 -1
- package/shared/index.js +398 -21
- package/le-space-ui-0.1.54.tgz +0 -0
package/shared/index.js
CHANGED
|
@@ -17,6 +17,16 @@ var RELAY_PING_IDLE_STATE = {
|
|
|
17
17
|
lastReceivedAt: null,
|
|
18
18
|
error: null
|
|
19
19
|
};
|
|
20
|
+
var IDLE_DEPLOYMENT_PROGRESS = {
|
|
21
|
+
stage: "idle",
|
|
22
|
+
label: "Ready",
|
|
23
|
+
progress: 0,
|
|
24
|
+
status: "info",
|
|
25
|
+
itemHash: null,
|
|
26
|
+
detail: null,
|
|
27
|
+
error: null,
|
|
28
|
+
timestamp: Date.now()
|
|
29
|
+
};
|
|
20
30
|
var REFRESH_INTERVAL_MS = 3e4;
|
|
21
31
|
var RELAY_PING_INTERVAL_MS = 2e4;
|
|
22
32
|
|
|
@@ -696,18 +706,34 @@ function resolveManifestSource(args) {
|
|
|
696
706
|
}
|
|
697
707
|
|
|
698
708
|
// src/shared/wallet-controller.ts
|
|
709
|
+
import { keccak_256 } from "@noble/hashes/sha3";
|
|
699
710
|
function getEthereumProvider() {
|
|
700
711
|
return globalThis.window?.ethereum ?? null;
|
|
701
712
|
}
|
|
713
|
+
function toChecksumAddress(address) {
|
|
714
|
+
if (!/^0x[0-9a-fA-F]{40}$/.test(address)) {
|
|
715
|
+
throw new Error("Invalid EVM address.");
|
|
716
|
+
}
|
|
717
|
+
const normalized = address.slice(2).toLowerCase();
|
|
718
|
+
const hash = Array.from(keccak_256(new TextEncoder().encode(normalized))).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
719
|
+
let result = "0x";
|
|
720
|
+
for (let index = 0; index < normalized.length; index += 1) {
|
|
721
|
+
result += Number.parseInt(hash[index], 16) >= 8 ? normalized[index].toUpperCase() : normalized[index];
|
|
722
|
+
}
|
|
723
|
+
return result;
|
|
724
|
+
}
|
|
702
725
|
async function connectWallet(provider = getEthereumProvider()) {
|
|
703
726
|
if (!provider) {
|
|
704
727
|
throw new Error("MetaMask provider not found.");
|
|
705
728
|
}
|
|
706
|
-
const accounts = await provider.request({
|
|
729
|
+
const accounts = await provider.request({
|
|
730
|
+
method: "eth_requestAccounts"
|
|
731
|
+
});
|
|
707
732
|
const chainId = await provider.request({ method: "eth_chainId" });
|
|
733
|
+
const address = accounts[0] ? toChecksumAddress(accounts[0]) : null;
|
|
708
734
|
return {
|
|
709
|
-
connected: Boolean(
|
|
710
|
-
address
|
|
735
|
+
connected: Boolean(address),
|
|
736
|
+
address,
|
|
711
737
|
chainId,
|
|
712
738
|
isMetaMask: Boolean(provider.isMetaMask)
|
|
713
739
|
};
|
|
@@ -963,6 +989,16 @@ async function createUnsignedForgetMessage(args) {
|
|
|
963
989
|
};
|
|
964
990
|
}
|
|
965
991
|
async function forgetAlephMessages(args) {
|
|
992
|
+
args.onProgress?.({
|
|
993
|
+
stage: "building-delete-message",
|
|
994
|
+
label: "Building delete message",
|
|
995
|
+
progress: 18,
|
|
996
|
+
status: "info",
|
|
997
|
+
itemHash: null,
|
|
998
|
+
detail: [...new Set((args.hashes ?? []).filter(Boolean))].join(", ") || null,
|
|
999
|
+
error: null,
|
|
1000
|
+
timestamp: Date.now()
|
|
1001
|
+
});
|
|
966
1002
|
const unsignedMessage = await createUnsignedForgetMessage({
|
|
967
1003
|
sender: args.sender,
|
|
968
1004
|
hashes: args.hashes,
|
|
@@ -972,18 +1008,49 @@ async function forgetAlephMessages(args) {
|
|
|
972
1008
|
channel: args.channel,
|
|
973
1009
|
now: args.now
|
|
974
1010
|
});
|
|
1011
|
+
args.onProgress?.({
|
|
1012
|
+
stage: "signing-delete-message",
|
|
1013
|
+
label: "Waiting for delete signature",
|
|
1014
|
+
progress: 42,
|
|
1015
|
+
status: "info",
|
|
1016
|
+
itemHash: unsignedMessage.item_hash,
|
|
1017
|
+
detail: unsignedMessage.item_hash,
|
|
1018
|
+
error: null,
|
|
1019
|
+
timestamp: Date.now()
|
|
1020
|
+
});
|
|
975
1021
|
const message = await signAlephMessage(unsignedMessage, args.signer);
|
|
1022
|
+
args.onProgress?.({
|
|
1023
|
+
stage: "broadcasting-delete",
|
|
1024
|
+
label: "Broadcasting delete to Aleph",
|
|
1025
|
+
progress: 68,
|
|
1026
|
+
status: "warning",
|
|
1027
|
+
itemHash: message.item_hash,
|
|
1028
|
+
detail: message.item_hash,
|
|
1029
|
+
error: null,
|
|
1030
|
+
timestamp: Date.now()
|
|
1031
|
+
});
|
|
976
1032
|
const { response, httpStatus } = await broadcastAlephMessage2(message, {
|
|
977
1033
|
apiHost: args.apiHost,
|
|
978
1034
|
sync: args.sync,
|
|
979
1035
|
fetch: args.fetch
|
|
980
1036
|
});
|
|
1037
|
+
const status = normalizeBroadcastStatus(httpStatus, response?.message_status);
|
|
1038
|
+
args.onProgress?.({
|
|
1039
|
+
stage: status === "rejected" ? "error" : "delete-completed",
|
|
1040
|
+
label: status === "rejected" ? "Delete rejected by Aleph" : "Delete submitted to Aleph",
|
|
1041
|
+
progress: 100,
|
|
1042
|
+
status: status === "rejected" ? "error" : "success",
|
|
1043
|
+
itemHash: message.item_hash,
|
|
1044
|
+
detail: status === "rejected" ? String(response?.details ?? "Aleph rejected this delete request.") : null,
|
|
1045
|
+
error: status === "rejected" ? String(response?.details ?? "Aleph rejected this delete request.") : null,
|
|
1046
|
+
timestamp: Date.now()
|
|
1047
|
+
});
|
|
981
1048
|
return {
|
|
982
1049
|
sender: args.sender,
|
|
983
1050
|
itemHash: message.item_hash,
|
|
984
1051
|
response,
|
|
985
1052
|
httpStatus,
|
|
986
|
-
status
|
|
1053
|
+
status
|
|
987
1054
|
};
|
|
988
1055
|
}
|
|
989
1056
|
|
|
@@ -1090,6 +1157,16 @@ async function createUnsignedInstanceMessage(args) {
|
|
|
1090
1157
|
};
|
|
1091
1158
|
}
|
|
1092
1159
|
async function deployInstance(args) {
|
|
1160
|
+
args.onProgress?.({
|
|
1161
|
+
stage: "building-message",
|
|
1162
|
+
label: "Building deployment message",
|
|
1163
|
+
progress: 32,
|
|
1164
|
+
status: "info",
|
|
1165
|
+
detail: args.content.metadata?.name ?? null,
|
|
1166
|
+
itemHash: null,
|
|
1167
|
+
error: null,
|
|
1168
|
+
timestamp: Date.now()
|
|
1169
|
+
});
|
|
1093
1170
|
const unsignedMessage = await createUnsignedInstanceMessage({
|
|
1094
1171
|
sender: args.sender,
|
|
1095
1172
|
content: args.content,
|
|
@@ -1097,17 +1174,47 @@ async function deployInstance(args) {
|
|
|
1097
1174
|
channel: args.channel,
|
|
1098
1175
|
now: args.now
|
|
1099
1176
|
});
|
|
1177
|
+
args.onProgress?.({
|
|
1178
|
+
stage: "signing-message",
|
|
1179
|
+
label: "Waiting for MetaMask signature",
|
|
1180
|
+
progress: 48,
|
|
1181
|
+
status: "info",
|
|
1182
|
+
detail: unsignedMessage.item_hash,
|
|
1183
|
+
itemHash: unsignedMessage.item_hash,
|
|
1184
|
+
error: null,
|
|
1185
|
+
timestamp: Date.now()
|
|
1186
|
+
});
|
|
1100
1187
|
const signature = await args.signer(unsignedMessage.sender, [unsignedMessage.chain, unsignedMessage.sender, unsignedMessage.type, unsignedMessage.item_hash].join("\n"));
|
|
1101
1188
|
const message = {
|
|
1102
1189
|
...unsignedMessage,
|
|
1103
1190
|
signature: signature.startsWith("0x") ? signature : `0x${signature}`
|
|
1104
1191
|
};
|
|
1192
|
+
args.onProgress?.({
|
|
1193
|
+
stage: "broadcasting",
|
|
1194
|
+
label: "Broadcasting to Aleph",
|
|
1195
|
+
progress: 64,
|
|
1196
|
+
status: "info",
|
|
1197
|
+
detail: message.item_hash,
|
|
1198
|
+
itemHash: message.item_hash,
|
|
1199
|
+
error: null,
|
|
1200
|
+
timestamp: Date.now()
|
|
1201
|
+
});
|
|
1105
1202
|
const { response, httpStatus } = await broadcastAlephMessage2(message, {
|
|
1106
1203
|
apiHost: args.apiHost,
|
|
1107
1204
|
sync: args.sync,
|
|
1108
1205
|
fetch: args.fetch
|
|
1109
1206
|
});
|
|
1110
1207
|
const status = httpStatus === 202 ? "pending" : typeof response.message_status === "string" ? response.message_status : "unknown";
|
|
1208
|
+
args.onProgress?.({
|
|
1209
|
+
stage: status === "processed" ? "deployment-confirmed" : status === "rejected" ? "deployment-rejected" : "waiting-for-aleph",
|
|
1210
|
+
label: status === "processed" ? "Deployment accepted by Aleph" : status === "rejected" ? "Deployment rejected by Aleph" : "Deployment submitted to Aleph",
|
|
1211
|
+
progress: status === "processed" ? 88 : status === "rejected" ? 100 : 78,
|
|
1212
|
+
status: status === "processed" ? "success" : status === "rejected" ? "error" : "warning",
|
|
1213
|
+
detail: status === "rejected" ? String(response.details ?? "Aleph rejected this deployment.") : null,
|
|
1214
|
+
itemHash: message.item_hash,
|
|
1215
|
+
error: status === "rejected" ? String(response.details ?? "Aleph rejected this deployment.") : null,
|
|
1216
|
+
timestamp: Date.now()
|
|
1217
|
+
});
|
|
1111
1218
|
return {
|
|
1112
1219
|
itemHash: message.item_hash,
|
|
1113
1220
|
httpStatus,
|
|
@@ -1118,6 +1225,107 @@ async function deployInstance(args) {
|
|
|
1118
1225
|
};
|
|
1119
1226
|
}
|
|
1120
1227
|
|
|
1228
|
+
// ../core/src/guest.ts
|
|
1229
|
+
async function notifyCrnAllocation2(args) {
|
|
1230
|
+
const normalizedCrnUrl = typeof args.crnUrl === "string" ? args.crnUrl.trim().replace(/\/+$/, "") : "";
|
|
1231
|
+
if (!normalizedCrnUrl) {
|
|
1232
|
+
return {
|
|
1233
|
+
status: "skipped",
|
|
1234
|
+
reason: "No CRN URL available for allocation notification."
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
try {
|
|
1238
|
+
const response = await args.fetch(
|
|
1239
|
+
`${normalizedCrnUrl}/control/allocation/notify`,
|
|
1240
|
+
{
|
|
1241
|
+
method: "POST",
|
|
1242
|
+
headers: {
|
|
1243
|
+
"content-type": "application/json"
|
|
1244
|
+
},
|
|
1245
|
+
body: JSON.stringify({ instance: args.itemHash })
|
|
1246
|
+
}
|
|
1247
|
+
);
|
|
1248
|
+
const payload = await response.json().catch(() => null);
|
|
1249
|
+
const responseText = payload == null ? "" : typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
1250
|
+
if (!response.ok) {
|
|
1251
|
+
return {
|
|
1252
|
+
status: "unconfirmed",
|
|
1253
|
+
reason: `CRN allocation notify returned ${response.status}${responseText ? ` ${responseText}` : ""}.`,
|
|
1254
|
+
payload
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
return {
|
|
1258
|
+
status: "confirmed",
|
|
1259
|
+
payload
|
|
1260
|
+
};
|
|
1261
|
+
} catch (error) {
|
|
1262
|
+
return {
|
|
1263
|
+
status: "unconfirmed",
|
|
1264
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
function isRetryableAllocationNotifyReason(reason) {
|
|
1269
|
+
const normalized = String(reason ?? "").toLowerCase();
|
|
1270
|
+
return normalized.includes("503") && (normalized.includes("node hash not yet discovered") || normalized.includes("cannot accept targeted allocations"));
|
|
1271
|
+
}
|
|
1272
|
+
async function notifyCrnAllocationWithRetry(args) {
|
|
1273
|
+
const attempts = Math.max(1, Number(args.attempts ?? 6));
|
|
1274
|
+
const delayMs = Math.max(0, Number(args.delayMs ?? 2e3));
|
|
1275
|
+
const sleep = args.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
1276
|
+
let lastResult = null;
|
|
1277
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
1278
|
+
args.onProgress?.({
|
|
1279
|
+
stage: "notifying-crn",
|
|
1280
|
+
label: "Notifying selected CRN",
|
|
1281
|
+
progress: 84,
|
|
1282
|
+
status: "info",
|
|
1283
|
+
itemHash: args.itemHash,
|
|
1284
|
+
detail: `${attempt + 1}/${attempts} ${String(args.crnUrl ?? "").trim() || "selected CRN"}`,
|
|
1285
|
+
error: null,
|
|
1286
|
+
timestamp: Date.now()
|
|
1287
|
+
});
|
|
1288
|
+
const result = await notifyCrnAllocation2({
|
|
1289
|
+
crnUrl: args.crnUrl,
|
|
1290
|
+
itemHash: args.itemHash,
|
|
1291
|
+
fetch: args.fetch
|
|
1292
|
+
});
|
|
1293
|
+
if (result.status === "confirmed" || result.status === "skipped") {
|
|
1294
|
+
return result;
|
|
1295
|
+
}
|
|
1296
|
+
lastResult = result;
|
|
1297
|
+
if (!isRetryableAllocationNotifyReason(result.reason)) {
|
|
1298
|
+
return result;
|
|
1299
|
+
}
|
|
1300
|
+
if (attempt < attempts - 1) {
|
|
1301
|
+
await sleep(delayMs);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
return lastResult ?? {
|
|
1305
|
+
status: "unconfirmed",
|
|
1306
|
+
reason: "CRN allocation notify was not confirmed."
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
// src/shared/events.ts
|
|
1311
|
+
function createDeploymentProgressEmitter() {
|
|
1312
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1313
|
+
return {
|
|
1314
|
+
emit(event) {
|
|
1315
|
+
listeners.forEach((listener) => listener(event));
|
|
1316
|
+
},
|
|
1317
|
+
subscribe(listener) {
|
|
1318
|
+
listeners.add(listener);
|
|
1319
|
+
return () => {
|
|
1320
|
+
listeners.delete(listener);
|
|
1321
|
+
};
|
|
1322
|
+
},
|
|
1323
|
+
clear() {
|
|
1324
|
+
listeners.clear();
|
|
1325
|
+
}
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1121
1329
|
// src/shared/controller.ts
|
|
1122
1330
|
function defaultState(props = {}) {
|
|
1123
1331
|
return {
|
|
@@ -1167,18 +1375,24 @@ function defaultState(props = {}) {
|
|
|
1167
1375
|
selectedCrn: null,
|
|
1168
1376
|
instances: [],
|
|
1169
1377
|
relayPing: RELAY_PING_IDLE_STATE,
|
|
1170
|
-
lastDeploymentHash: null
|
|
1378
|
+
lastDeploymentHash: null,
|
|
1379
|
+
deploymentProgress: IDLE_DEPLOYMENT_PROGRESS
|
|
1171
1380
|
};
|
|
1172
1381
|
}
|
|
1173
1382
|
async function sha256Hex(payload) {
|
|
1174
|
-
const digest = await globalThis.crypto.subtle.digest(
|
|
1383
|
+
const digest = await globalThis.crypto.subtle.digest(
|
|
1384
|
+
"SHA-256",
|
|
1385
|
+
new TextEncoder().encode(payload)
|
|
1386
|
+
);
|
|
1175
1387
|
return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1176
1388
|
}
|
|
1177
1389
|
function mappedPorts(execution) {
|
|
1178
|
-
return Object.entries(execution?.networking?.mapped_ports ?? {}).map(
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1390
|
+
return Object.entries(execution?.networking?.mapped_ports ?? {}).map(
|
|
1391
|
+
([port, mapping]) => ({
|
|
1392
|
+
label: `${port}/${mapping.udp ? "udp" : "tcp"}`,
|
|
1393
|
+
hostPort: mapping.host ?? null
|
|
1394
|
+
})
|
|
1395
|
+
);
|
|
1182
1396
|
}
|
|
1183
1397
|
function rootfsHealth(args) {
|
|
1184
1398
|
if (!args.manifestState.valid || !args.manifestState.manifest) {
|
|
@@ -1236,7 +1450,9 @@ async function resolveManifest(args) {
|
|
|
1236
1450
|
}
|
|
1237
1451
|
async function inspectInstanceRuntime(args) {
|
|
1238
1452
|
const details = {
|
|
1239
|
-
messageStatus: String(
|
|
1453
|
+
messageStatus: String(
|
|
1454
|
+
args.instance.status ?? (args.instance.confirmed ? "processed" : "unknown")
|
|
1455
|
+
).toLowerCase(),
|
|
1240
1456
|
allocationSource: null,
|
|
1241
1457
|
crnUrl: null,
|
|
1242
1458
|
hostIpv4: null,
|
|
@@ -1267,11 +1483,15 @@ async function inspectInstanceRuntime(args) {
|
|
|
1267
1483
|
details.allocationSource = allocation?.source ?? null;
|
|
1268
1484
|
details.crnUrl = allocation?.crnUrl ?? null;
|
|
1269
1485
|
details.ipv6 = allocation?.vmIpv6 ?? null;
|
|
1270
|
-
details.webUrl = await args.client.fetch2n6WebAccessUrl(
|
|
1486
|
+
details.webUrl = await args.client.fetch2n6WebAccessUrl(
|
|
1487
|
+
args.instance.item_hash
|
|
1488
|
+
);
|
|
1271
1489
|
if (!allocation?.crnUrl) {
|
|
1272
1490
|
return details;
|
|
1273
1491
|
}
|
|
1274
|
-
const executionLookup = await args.client.fetchCrnExecutionMap(
|
|
1492
|
+
const executionLookup = await args.client.fetchCrnExecutionMap(
|
|
1493
|
+
allocation.crnUrl
|
|
1494
|
+
);
|
|
1275
1495
|
const executionPayload = executionLookup.payload?.[args.instance.item_hash];
|
|
1276
1496
|
if (!executionPayload) {
|
|
1277
1497
|
return details;
|
|
@@ -1299,7 +1519,10 @@ function compatibleCrnsForTier(crns, state) {
|
|
|
1299
1519
|
if (!state.pricingSummary.pricing || !state.pricingSummary.tier) {
|
|
1300
1520
|
return [];
|
|
1301
1521
|
}
|
|
1302
|
-
const spec = tierSpec(
|
|
1522
|
+
const spec = tierSpec(
|
|
1523
|
+
state.pricingSummary.pricing,
|
|
1524
|
+
state.pricingSummary.tier
|
|
1525
|
+
);
|
|
1303
1526
|
return filterDeployableCrns(crns, { spec });
|
|
1304
1527
|
}
|
|
1305
1528
|
async function pingPeer(libp2p) {
|
|
@@ -1361,6 +1584,7 @@ var SponsorRelayController = class {
|
|
|
1361
1584
|
pingTimer = null;
|
|
1362
1585
|
stopWalletWatch = null;
|
|
1363
1586
|
props;
|
|
1587
|
+
progressEmitter = createDeploymentProgressEmitter();
|
|
1364
1588
|
constructor(props = {}) {
|
|
1365
1589
|
this.props = props;
|
|
1366
1590
|
this.state = defaultState(props);
|
|
@@ -1371,6 +1595,9 @@ var SponsorRelayController = class {
|
|
|
1371
1595
|
twoN6ApiHost: props.twoN6ApiHost
|
|
1372
1596
|
});
|
|
1373
1597
|
}
|
|
1598
|
+
subscribeToDeploymentProgress(listener) {
|
|
1599
|
+
return this.progressEmitter.subscribe(listener);
|
|
1600
|
+
}
|
|
1374
1601
|
subscribe(subscriber) {
|
|
1375
1602
|
this.subscribers.add(subscriber);
|
|
1376
1603
|
return () => {
|
|
@@ -1395,6 +1622,18 @@ var SponsorRelayController = class {
|
|
|
1395
1622
|
};
|
|
1396
1623
|
this.emit();
|
|
1397
1624
|
}
|
|
1625
|
+
emitProgress(event) {
|
|
1626
|
+
const nextEvent = {
|
|
1627
|
+
...event,
|
|
1628
|
+
timestamp: Date.now()
|
|
1629
|
+
};
|
|
1630
|
+
this.patch({
|
|
1631
|
+
deploymentProgress: nextEvent,
|
|
1632
|
+
statusText: event.error ?? event.detail ?? event.label,
|
|
1633
|
+
errorText: event.status === "error" ? event.error ?? event.detail ?? event.label : this.state.errorText
|
|
1634
|
+
});
|
|
1635
|
+
this.progressEmitter.emit(nextEvent);
|
|
1636
|
+
}
|
|
1398
1637
|
async init() {
|
|
1399
1638
|
this.stopWalletWatch = watchWallet(() => {
|
|
1400
1639
|
void this.refreshWalletDerivedState();
|
|
@@ -1514,11 +1753,25 @@ var SponsorRelayController = class {
|
|
|
1514
1753
|
statusText: "Refreshing relay sponsor data"
|
|
1515
1754
|
});
|
|
1516
1755
|
try {
|
|
1756
|
+
this.emitProgress({
|
|
1757
|
+
stage: "loading-manifest",
|
|
1758
|
+
label: "Loading manifest",
|
|
1759
|
+
progress: 8,
|
|
1760
|
+
status: "info",
|
|
1761
|
+
detail: this.state.manifestUrl
|
|
1762
|
+
});
|
|
1517
1763
|
const manifestState = await resolveManifest({
|
|
1518
1764
|
manifestUrl: this.state.manifestUrl,
|
|
1519
1765
|
manifestJson: this.state.manifestJson
|
|
1520
1766
|
});
|
|
1521
1767
|
const manifest = manifestState.manifest;
|
|
1768
|
+
this.emitProgress({
|
|
1769
|
+
stage: "loading-pricing",
|
|
1770
|
+
label: "Loading pricing and CRNs",
|
|
1771
|
+
progress: 18,
|
|
1772
|
+
status: "info",
|
|
1773
|
+
detail: "Fetching live Aleph pricing and compatible CRNs."
|
|
1774
|
+
});
|
|
1522
1775
|
const [pricingSummary, crns] = await Promise.all([
|
|
1523
1776
|
fetchInstancePricing(this.client.apiHost),
|
|
1524
1777
|
this.client.fetchCrns()
|
|
@@ -1526,6 +1779,13 @@ var SponsorRelayController = class {
|
|
|
1526
1779
|
let balance = this.state.balance;
|
|
1527
1780
|
let instances = [];
|
|
1528
1781
|
if (this.state.wallet.address) {
|
|
1782
|
+
this.emitProgress({
|
|
1783
|
+
stage: "loading-balance",
|
|
1784
|
+
label: "Loading wallet balance",
|
|
1785
|
+
progress: 28,
|
|
1786
|
+
status: "info",
|
|
1787
|
+
detail: this.state.wallet.address
|
|
1788
|
+
});
|
|
1529
1789
|
const [nextBalance, rawInstances] = await Promise.all([
|
|
1530
1790
|
this.client.fetchBalance(this.state.wallet.address),
|
|
1531
1791
|
this.state.showInstances ? this.client.fetchInstances(this.state.wallet.address) : Promise.resolve([])
|
|
@@ -1545,8 +1805,21 @@ var SponsorRelayController = class {
|
|
|
1545
1805
|
let rootfsVerified = false;
|
|
1546
1806
|
let rootfsResolution = null;
|
|
1547
1807
|
if (manifestState.valid && manifest) {
|
|
1548
|
-
|
|
1549
|
-
|
|
1808
|
+
this.emitProgress({
|
|
1809
|
+
stage: "verifying-rootfs",
|
|
1810
|
+
label: "Verifying rootfs",
|
|
1811
|
+
progress: 38,
|
|
1812
|
+
status: "info",
|
|
1813
|
+
detail: manifest.rootfsItemHash
|
|
1814
|
+
});
|
|
1815
|
+
rootfsVerified = await verifyRootfsExists(
|
|
1816
|
+
manifest.rootfsItemHash,
|
|
1817
|
+
this.client.apiHost
|
|
1818
|
+
);
|
|
1819
|
+
rootfsResolution = await resolveRootfsReference(
|
|
1820
|
+
manifest.rootfsItemHash,
|
|
1821
|
+
this.client.apiHost
|
|
1822
|
+
);
|
|
1550
1823
|
}
|
|
1551
1824
|
this.patch({
|
|
1552
1825
|
manifestState,
|
|
@@ -1569,12 +1842,26 @@ var SponsorRelayController = class {
|
|
|
1569
1842
|
statusText: "Relay sponsor data ready"
|
|
1570
1843
|
});
|
|
1571
1844
|
this.recomputePricingSummary();
|
|
1845
|
+
this.emitProgress({
|
|
1846
|
+
stage: "completed",
|
|
1847
|
+
label: "Data ready",
|
|
1848
|
+
progress: 100,
|
|
1849
|
+
status: "success",
|
|
1850
|
+
detail: "Manifest, pricing, and wallet state are ready."
|
|
1851
|
+
});
|
|
1572
1852
|
} catch (error) {
|
|
1573
1853
|
this.patch({
|
|
1574
1854
|
busy: { refreshing: false },
|
|
1575
1855
|
errorText: error instanceof Error ? error.message : String(error),
|
|
1576
1856
|
statusText: "Refresh failed"
|
|
1577
1857
|
});
|
|
1858
|
+
this.emitProgress({
|
|
1859
|
+
stage: "error",
|
|
1860
|
+
label: "Refresh failed",
|
|
1861
|
+
progress: 100,
|
|
1862
|
+
status: "error",
|
|
1863
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1864
|
+
});
|
|
1578
1865
|
}
|
|
1579
1866
|
}
|
|
1580
1867
|
async refreshRelayPing() {
|
|
@@ -1587,7 +1874,9 @@ var SponsorRelayController = class {
|
|
|
1587
1874
|
return;
|
|
1588
1875
|
}
|
|
1589
1876
|
if (!this.state.manifest || !this.state.rootfsVerified || !this.state.pricingSummary.pricing || !this.state.pricingSummary.tier) {
|
|
1590
|
-
this.patch({
|
|
1877
|
+
this.patch({
|
|
1878
|
+
errorText: "Manifest, rootfs, and pricing must be ready before deploying."
|
|
1879
|
+
});
|
|
1591
1880
|
return;
|
|
1592
1881
|
}
|
|
1593
1882
|
this.patch({
|
|
@@ -1596,13 +1885,33 @@ var SponsorRelayController = class {
|
|
|
1596
1885
|
statusText: "Broadcasting deployment"
|
|
1597
1886
|
});
|
|
1598
1887
|
try {
|
|
1599
|
-
|
|
1888
|
+
this.emitProgress({
|
|
1889
|
+
stage: "validating",
|
|
1890
|
+
label: "Validating deployment",
|
|
1891
|
+
progress: 5,
|
|
1892
|
+
status: "info",
|
|
1893
|
+
detail: "Checking wallet, manifest, rootfs, pricing, and SSH key."
|
|
1894
|
+
});
|
|
1895
|
+
const spec = tierSpec(
|
|
1896
|
+
this.state.pricingSummary.pricing,
|
|
1897
|
+
this.state.pricingSummary.tier
|
|
1898
|
+
);
|
|
1899
|
+
this.emitProgress({
|
|
1900
|
+
stage: "selecting-crn",
|
|
1901
|
+
label: "Selecting CRN",
|
|
1902
|
+
progress: 18,
|
|
1903
|
+
status: "info",
|
|
1904
|
+
detail: this.state.selectedCrn?.name ?? this.state.selectedCrn?.hash ?? "Auto-selected compatible CRN"
|
|
1905
|
+
});
|
|
1600
1906
|
const content = createInstanceContent({
|
|
1601
1907
|
address: this.state.wallet.address,
|
|
1602
1908
|
name: this.state.instanceName.trim(),
|
|
1603
1909
|
sshPublicKey: this.state.sshPublicKey.trim(),
|
|
1604
1910
|
rootfsItemHash: this.state.manifest.rootfsItemHash,
|
|
1605
|
-
rootfsSizeMiB: Math.max(
|
|
1911
|
+
rootfsSizeMiB: Math.max(
|
|
1912
|
+
this.state.manifest.rootfsSizeMiB,
|
|
1913
|
+
spec.diskMiB
|
|
1914
|
+
),
|
|
1606
1915
|
vcpus: spec.vcpus,
|
|
1607
1916
|
memoryMiB: spec.memoryMiB,
|
|
1608
1917
|
rootfsVersion: this.state.manifest.version,
|
|
@@ -1615,20 +1924,57 @@ var SponsorRelayController = class {
|
|
|
1615
1924
|
signer: personalSign,
|
|
1616
1925
|
fetch: (url, init) => fetch(url, init),
|
|
1617
1926
|
apiHost: this.client.apiHost,
|
|
1618
|
-
sync: false
|
|
1927
|
+
sync: false,
|
|
1928
|
+
onProgress: (event) => {
|
|
1929
|
+
this.emitProgress(event);
|
|
1930
|
+
}
|
|
1619
1931
|
});
|
|
1620
1932
|
this.patch({
|
|
1621
1933
|
busy: { deploying: false },
|
|
1622
1934
|
statusText: `Deployment submitted: ${result.itemHash}`,
|
|
1623
1935
|
lastDeploymentHash: result.itemHash
|
|
1624
1936
|
});
|
|
1937
|
+
if (result.status === "processed" && this.state.selectedCrn?.address) {
|
|
1938
|
+
await notifyCrnAllocationWithRetry({
|
|
1939
|
+
crnUrl: this.state.selectedCrn.address,
|
|
1940
|
+
itemHash: result.itemHash,
|
|
1941
|
+
fetch: (url, init) => fetch(url, init),
|
|
1942
|
+
onProgress: (event) => {
|
|
1943
|
+
this.emitProgress(event);
|
|
1944
|
+
}
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
this.emitProgress({
|
|
1948
|
+
stage: "refreshing-instances",
|
|
1949
|
+
label: "Refreshing instances",
|
|
1950
|
+
progress: 92,
|
|
1951
|
+
status: "info",
|
|
1952
|
+
itemHash: result.itemHash,
|
|
1953
|
+
detail: "Reloading deployments and runtime state."
|
|
1954
|
+
});
|
|
1625
1955
|
await this.refresh();
|
|
1956
|
+
this.emitProgress({
|
|
1957
|
+
stage: "completed",
|
|
1958
|
+
label: "Deployment completed",
|
|
1959
|
+
progress: 100,
|
|
1960
|
+
status: result.status === "rejected" ? "error" : "success",
|
|
1961
|
+
itemHash: result.itemHash,
|
|
1962
|
+
detail: result.status === "rejected" ? result.rejectionReason ?? "Aleph rejected the deployment." : "Deployment submitted and synced back into the panel.",
|
|
1963
|
+
error: result.status === "rejected" ? result.rejectionReason ?? "Aleph rejected the deployment." : null
|
|
1964
|
+
});
|
|
1626
1965
|
} catch (error) {
|
|
1627
1966
|
this.patch({
|
|
1628
1967
|
busy: { deploying: false },
|
|
1629
1968
|
errorText: error instanceof Error ? error.message : String(error),
|
|
1630
1969
|
statusText: "Deployment failed"
|
|
1631
1970
|
});
|
|
1971
|
+
this.emitProgress({
|
|
1972
|
+
stage: "error",
|
|
1973
|
+
label: "Deployment failed",
|
|
1974
|
+
progress: 100,
|
|
1975
|
+
status: "error",
|
|
1976
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1977
|
+
});
|
|
1632
1978
|
}
|
|
1633
1979
|
}
|
|
1634
1980
|
async deleteInstance(instanceHash) {
|
|
@@ -1642,6 +1988,14 @@ var SponsorRelayController = class {
|
|
|
1642
1988
|
statusText: `Deleting ${instanceHash}`
|
|
1643
1989
|
});
|
|
1644
1990
|
try {
|
|
1991
|
+
this.emitProgress({
|
|
1992
|
+
stage: "validating",
|
|
1993
|
+
label: "Validating delete request",
|
|
1994
|
+
progress: 6,
|
|
1995
|
+
status: "warning",
|
|
1996
|
+
itemHash: instanceHash,
|
|
1997
|
+
detail: "Preparing Aleph FORGET message."
|
|
1998
|
+
});
|
|
1645
1999
|
await forgetAlephMessages({
|
|
1646
2000
|
sender: this.state.wallet.address,
|
|
1647
2001
|
hashes: [instanceHash],
|
|
@@ -1653,13 +2007,33 @@ var SponsorRelayController = class {
|
|
|
1653
2007
|
status: response.status,
|
|
1654
2008
|
json: async () => await response.json()
|
|
1655
2009
|
})),
|
|
1656
|
-
apiHost: this.client.apiHost
|
|
2010
|
+
apiHost: this.client.apiHost,
|
|
2011
|
+
onProgress: (event) => {
|
|
2012
|
+
this.emitProgress(event);
|
|
2013
|
+
}
|
|
1657
2014
|
});
|
|
1658
2015
|
this.patch({
|
|
1659
2016
|
busy: { deletingInstanceHash: null },
|
|
1660
2017
|
statusText: `Deletion submitted for ${instanceHash}`
|
|
1661
2018
|
});
|
|
2019
|
+
this.emitProgress({
|
|
2020
|
+
stage: "refreshing-instances",
|
|
2021
|
+
label: "Refreshing instances after delete",
|
|
2022
|
+
progress: 92,
|
|
2023
|
+
status: "info",
|
|
2024
|
+
itemHash: instanceHash,
|
|
2025
|
+
detail: "Reloading current deployments."
|
|
2026
|
+
});
|
|
1662
2027
|
await this.refresh();
|
|
2028
|
+
this.emitProgress({
|
|
2029
|
+
stage: "completed",
|
|
2030
|
+
label: "Delete completed",
|
|
2031
|
+
progress: 100,
|
|
2032
|
+
status: "success",
|
|
2033
|
+
itemHash: instanceHash,
|
|
2034
|
+
detail: "Delete request submitted and deployments refreshed.",
|
|
2035
|
+
error: null
|
|
2036
|
+
});
|
|
1663
2037
|
} catch (error) {
|
|
1664
2038
|
this.patch({
|
|
1665
2039
|
busy: { deletingInstanceHash: null },
|
|
@@ -1676,6 +2050,7 @@ export {
|
|
|
1676
2050
|
DEFAULT_INSTANCE_NAME,
|
|
1677
2051
|
DEFAULT_MANIFEST_URL,
|
|
1678
2052
|
DEFAULT_TIER_ID,
|
|
2053
|
+
IDLE_DEPLOYMENT_PROGRESS,
|
|
1679
2054
|
REFRESH_INTERVAL_MS,
|
|
1680
2055
|
RELAY_PING_IDLE_STATE,
|
|
1681
2056
|
RELAY_PING_INTERVAL_MS,
|
|
@@ -1683,6 +2058,7 @@ export {
|
|
|
1683
2058
|
SponsorRelayController,
|
|
1684
2059
|
buildSshCommand,
|
|
1685
2060
|
connectWallet,
|
|
2061
|
+
createDeploymentProgressEmitter,
|
|
1686
2062
|
createSponsorRelayController,
|
|
1687
2063
|
formatDateTime,
|
|
1688
2064
|
formatNumber,
|
|
@@ -1691,5 +2067,6 @@ export {
|
|
|
1691
2067
|
personalSign,
|
|
1692
2068
|
resolveManifestSource,
|
|
1693
2069
|
shortHash,
|
|
2070
|
+
toChecksumAddress,
|
|
1694
2071
|
watchWallet
|
|
1695
2072
|
};
|
package/le-space-ui-0.1.54.tgz
DELETED
|
Binary file
|