@le-space/ui 0.1.62 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/react/index.d.ts +10 -1
- package/react/index.js +718 -24
- package/shared/index.d.ts +10 -1
- package/shared/index.js +711 -23
package/react/index.js
CHANGED
|
@@ -1832,6 +1832,28 @@ async function waitForVmRuntime(args) {
|
|
|
1832
1832
|
}
|
|
1833
1833
|
|
|
1834
1834
|
// ../core/src/guest.ts
|
|
1835
|
+
async function defaultHttpProbe(fetch2, url, timeoutMs = 1e4) {
|
|
1836
|
+
try {
|
|
1837
|
+
const controller = new AbortController();
|
|
1838
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
1839
|
+
const response = await fetch2(url, {
|
|
1840
|
+
method: "GET",
|
|
1841
|
+
redirect: "follow",
|
|
1842
|
+
signal: controller.signal
|
|
1843
|
+
});
|
|
1844
|
+
clearTimeout(timeout);
|
|
1845
|
+
return {
|
|
1846
|
+
ok: response.ok,
|
|
1847
|
+
status: response.status,
|
|
1848
|
+
url: response.url
|
|
1849
|
+
};
|
|
1850
|
+
} catch (error) {
|
|
1851
|
+
return {
|
|
1852
|
+
ok: false,
|
|
1853
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1835
1857
|
async function notifyCrnAllocation2(args) {
|
|
1836
1858
|
const normalizedCrnUrl = typeof args.crnUrl === "string" ? args.crnUrl.trim().replace(/\/+$/, "") : "";
|
|
1837
1859
|
if (!normalizedCrnUrl) {
|
|
@@ -1912,6 +1934,324 @@ async function notifyCrnAllocationWithRetry(args) {
|
|
|
1912
1934
|
reason: "CRN allocation notify was not confirmed."
|
|
1913
1935
|
};
|
|
1914
1936
|
}
|
|
1937
|
+
async function waitForSetupEndpoint(args) {
|
|
1938
|
+
const attempts = Math.max(1, Number(args.attempts ?? 15));
|
|
1939
|
+
const delayMs = Math.max(0, Number(args.delayMs ?? 4e3));
|
|
1940
|
+
const sleep = args.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
1941
|
+
const url = `http://${args.hostIpv4}:${args.setupPort}/health`;
|
|
1942
|
+
let result = await defaultHttpProbe(
|
|
1943
|
+
args.fetch,
|
|
1944
|
+
url,
|
|
1945
|
+
Number(args.httpTimeoutMs ?? 1e4)
|
|
1946
|
+
);
|
|
1947
|
+
args.onAttempt?.(result, 1, attempts);
|
|
1948
|
+
for (let attempt = 1; attempt < attempts; attempt += 1) {
|
|
1949
|
+
if (result.ok) return result;
|
|
1950
|
+
await sleep(delayMs);
|
|
1951
|
+
result = await defaultHttpProbe(
|
|
1952
|
+
args.fetch,
|
|
1953
|
+
url,
|
|
1954
|
+
Number(args.httpTimeoutMs ?? 1e4)
|
|
1955
|
+
);
|
|
1956
|
+
args.onAttempt?.(result, attempt + 1, attempts);
|
|
1957
|
+
}
|
|
1958
|
+
return result;
|
|
1959
|
+
}
|
|
1960
|
+
async function configureUcGoPeer(args) {
|
|
1961
|
+
const controller = new AbortController();
|
|
1962
|
+
const timeout = setTimeout(
|
|
1963
|
+
() => controller.abort(),
|
|
1964
|
+
Number(args.timeoutMs ?? 18e4)
|
|
1965
|
+
);
|
|
1966
|
+
const payload = {
|
|
1967
|
+
public_ipv4: args.hostIpv4,
|
|
1968
|
+
public_ipv6: args.publicIpv6 ?? void 0,
|
|
1969
|
+
proxy_url: args.proxyUrl ?? void 0,
|
|
1970
|
+
tcp_port: args.tcpPort ?? void 0,
|
|
1971
|
+
ws_port: args.wsPort ?? void 0,
|
|
1972
|
+
udp_port: args.udpPort ?? void 0,
|
|
1973
|
+
quic_port: args.quicPort ?? void 0,
|
|
1974
|
+
webrtc_port: args.webrtcPort ?? void 0
|
|
1975
|
+
};
|
|
1976
|
+
try {
|
|
1977
|
+
const response = await args.fetch(
|
|
1978
|
+
`http://${args.hostIpv4}:${args.setupPort}/configure`,
|
|
1979
|
+
{
|
|
1980
|
+
method: "POST",
|
|
1981
|
+
headers: {
|
|
1982
|
+
"content-type": "application/json"
|
|
1983
|
+
},
|
|
1984
|
+
body: JSON.stringify(payload),
|
|
1985
|
+
signal: controller.signal
|
|
1986
|
+
}
|
|
1987
|
+
);
|
|
1988
|
+
const responsePayload = await response.json().catch(() => null);
|
|
1989
|
+
if (!response.ok) {
|
|
1990
|
+
throw new Error(
|
|
1991
|
+
`Relay setup request failed: ${response.status} ${typeof responsePayload === "string" ? responsePayload : JSON.stringify(responsePayload ?? {})}`
|
|
1992
|
+
);
|
|
1993
|
+
}
|
|
1994
|
+
return responsePayload;
|
|
1995
|
+
} finally {
|
|
1996
|
+
clearTimeout(timeout);
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
async function configureOrbitdbRelaySetup2(args) {
|
|
2000
|
+
const controller = new AbortController();
|
|
2001
|
+
const timeout = setTimeout(
|
|
2002
|
+
() => controller.abort(),
|
|
2003
|
+
Number(args.timeoutMs ?? 18e4)
|
|
2004
|
+
);
|
|
2005
|
+
const payload = {
|
|
2006
|
+
public_ipv4: args.hostIpv4,
|
|
2007
|
+
public_ipv6: args.publicIpv6 ?? void 0,
|
|
2008
|
+
tcp_port: args.tcpPort,
|
|
2009
|
+
ws_port: args.wsPort,
|
|
2010
|
+
proxy_url: args.proxyUrl ?? void 0,
|
|
2011
|
+
metrics_port: args.metricsPort ?? void 0,
|
|
2012
|
+
metrics_https_port: args.metricsHttpsPort ?? void 0,
|
|
2013
|
+
webrtc_port: args.webrtcPort ?? void 0,
|
|
2014
|
+
quic_port: args.quicPort ?? void 0
|
|
2015
|
+
};
|
|
2016
|
+
try {
|
|
2017
|
+
const response = await args.fetch(
|
|
2018
|
+
`http://${args.hostIpv4}:${args.setupPort}/configure`,
|
|
2019
|
+
{
|
|
2020
|
+
method: "POST",
|
|
2021
|
+
headers: {
|
|
2022
|
+
"content-type": "application/json"
|
|
2023
|
+
},
|
|
2024
|
+
body: JSON.stringify(payload),
|
|
2025
|
+
signal: controller.signal
|
|
2026
|
+
}
|
|
2027
|
+
);
|
|
2028
|
+
const responsePayload = await response.json().catch(() => null);
|
|
2029
|
+
if (!response.ok) {
|
|
2030
|
+
throw new Error(
|
|
2031
|
+
`Relay setup request failed: ${response.status} ${typeof responsePayload === "string" ? responsePayload : JSON.stringify(responsePayload ?? {})}`
|
|
2032
|
+
);
|
|
2033
|
+
}
|
|
2034
|
+
return responsePayload;
|
|
2035
|
+
} finally {
|
|
2036
|
+
clearTimeout(timeout);
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
async function fetchUcGoPeerMetadata(args) {
|
|
2040
|
+
const attempts = Math.max(1, Number(args.attempts ?? 60));
|
|
2041
|
+
const delayMs = Math.max(0, Number(args.delayMs ?? 3e3));
|
|
2042
|
+
const sleep = args.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
2043
|
+
let lastPayload = null;
|
|
2044
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
2045
|
+
const controller = new AbortController();
|
|
2046
|
+
const timeout = setTimeout(
|
|
2047
|
+
() => controller.abort(),
|
|
2048
|
+
Number(args.timeoutMs ?? 18e4)
|
|
2049
|
+
);
|
|
2050
|
+
try {
|
|
2051
|
+
const response = await args.fetch(
|
|
2052
|
+
`http://${args.hostIpv4}:${args.setupPort}/metadata`,
|
|
2053
|
+
{
|
|
2054
|
+
signal: controller.signal
|
|
2055
|
+
}
|
|
2056
|
+
);
|
|
2057
|
+
const payload = await response.json().catch(() => null);
|
|
2058
|
+
lastPayload = payload;
|
|
2059
|
+
args.onAttempt?.(
|
|
2060
|
+
payload,
|
|
2061
|
+
Boolean(
|
|
2062
|
+
response.ok && payload && typeof payload === "object" && payload.status === "ready"
|
|
2063
|
+
),
|
|
2064
|
+
attempt + 1,
|
|
2065
|
+
attempts
|
|
2066
|
+
);
|
|
2067
|
+
if (response.ok && payload && typeof payload === "object" && payload.status === "ready") {
|
|
2068
|
+
return payload;
|
|
2069
|
+
}
|
|
2070
|
+
if (response.status >= 500) {
|
|
2071
|
+
throw new Error(
|
|
2072
|
+
`Relay metadata request failed: ${response.status} ${typeof payload === "string" ? payload : JSON.stringify(payload ?? {})}`
|
|
2073
|
+
);
|
|
2074
|
+
}
|
|
2075
|
+
} finally {
|
|
2076
|
+
clearTimeout(timeout);
|
|
2077
|
+
}
|
|
2078
|
+
if (attempt < attempts - 1) {
|
|
2079
|
+
await sleep(delayMs);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
throw new Error(
|
|
2083
|
+
`Relay metadata did not become ready after ${attempts} attempts: ${typeof lastPayload === "string" ? lastPayload : JSON.stringify(lastPayload ?? {})}`
|
|
2084
|
+
);
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
// ../aleph-bootstrap/src/index.ts
|
|
2088
|
+
var DEFAULT_ALEPH_API_HOST3 = "https://api2.aleph.im";
|
|
2089
|
+
var DEFAULT_ALEPH_BOOTSTRAP_CHANNEL = "simple-todo";
|
|
2090
|
+
var DEFAULT_ALEPH_BOOTSTRAP_REF = "simple-todo-bootstrap";
|
|
2091
|
+
var DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE = "relay-bootstrap";
|
|
2092
|
+
var DEFAULT_BOOTSTRAP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2093
|
+
function normalizeHost(host) {
|
|
2094
|
+
return host.trim().toLowerCase();
|
|
2095
|
+
}
|
|
2096
|
+
function splitMultiaddr(addr) {
|
|
2097
|
+
return addr.split("/").filter(Boolean);
|
|
2098
|
+
}
|
|
2099
|
+
function isPrivateIpv4(ip) {
|
|
2100
|
+
const parts = ip.split(".").map((part) => Number(part));
|
|
2101
|
+
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
|
|
2102
|
+
return false;
|
|
2103
|
+
}
|
|
2104
|
+
if (parts[0] === 10) return true;
|
|
2105
|
+
if (parts[0] === 127) return true;
|
|
2106
|
+
if (parts[0] === 0) return true;
|
|
2107
|
+
if (parts[0] === 169 && parts[1] === 254) return true;
|
|
2108
|
+
if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) return true;
|
|
2109
|
+
if (parts[0] === 192 && parts[1] === 168) return true;
|
|
2110
|
+
if (parts[0] === 100 && parts[1] >= 64 && parts[1] <= 127) return true;
|
|
2111
|
+
return false;
|
|
2112
|
+
}
|
|
2113
|
+
function isPrivateIpv6(ip) {
|
|
2114
|
+
const normalized = ip.trim().toLowerCase();
|
|
2115
|
+
return normalized === "::1" || normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("fe8") || normalized.startsWith("fe9") || normalized.startsWith("fea") || normalized.startsWith("feb");
|
|
2116
|
+
}
|
|
2117
|
+
function isLocalHostname(host) {
|
|
2118
|
+
const normalized = normalizeHost(host);
|
|
2119
|
+
return normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local");
|
|
2120
|
+
}
|
|
2121
|
+
function hasPeerId(addr) {
|
|
2122
|
+
return splitMultiaddr(addr).includes("p2p");
|
|
2123
|
+
}
|
|
2124
|
+
function isBrowserDialableMultiaddr(addr) {
|
|
2125
|
+
const normalized = addr.toLowerCase();
|
|
2126
|
+
return normalized.includes("/ws") || normalized.includes("/wss") || normalized.includes("/webtransport") || normalized.includes("/webrtc-direct");
|
|
2127
|
+
}
|
|
2128
|
+
function dedupeMultiaddrs(addrs) {
|
|
2129
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2130
|
+
const result = [];
|
|
2131
|
+
for (const value of addrs) {
|
|
2132
|
+
const trimmed = value.trim();
|
|
2133
|
+
if (!trimmed || seen.has(trimmed)) continue;
|
|
2134
|
+
seen.add(trimmed);
|
|
2135
|
+
result.push(trimmed);
|
|
2136
|
+
}
|
|
2137
|
+
return result;
|
|
2138
|
+
}
|
|
2139
|
+
function isPublicMultiaddr(addr) {
|
|
2140
|
+
const parts = splitMultiaddr(addr);
|
|
2141
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
2142
|
+
const protocol = parts[index];
|
|
2143
|
+
const value = parts[index + 1];
|
|
2144
|
+
if (protocol === "ip4" && value) {
|
|
2145
|
+
return !isPrivateIpv4(value);
|
|
2146
|
+
}
|
|
2147
|
+
if (protocol === "ip6" && value) {
|
|
2148
|
+
return !isPrivateIpv6(value);
|
|
2149
|
+
}
|
|
2150
|
+
if ((protocol === "dns4" || protocol === "dns6" || protocol === "dnsaddr") && value) {
|
|
2151
|
+
return !isLocalHostname(value);
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
return false;
|
|
2155
|
+
}
|
|
2156
|
+
function filterPublicMultiaddrs(addrs, options = {}) {
|
|
2157
|
+
return dedupeMultiaddrs(addrs).filter((addr) => {
|
|
2158
|
+
if (!isPublicMultiaddr(addr)) return false;
|
|
2159
|
+
if (options.requirePeerId !== false && !hasPeerId(addr)) return false;
|
|
2160
|
+
if (options.browserDialableOnly && !isBrowserDialableMultiaddr(addr)) {
|
|
2161
|
+
return false;
|
|
2162
|
+
}
|
|
2163
|
+
return true;
|
|
2164
|
+
});
|
|
2165
|
+
}
|
|
2166
|
+
function buildRelayBootstrapPostContent(args) {
|
|
2167
|
+
const now = args.now ?? Date.now() / 1e3;
|
|
2168
|
+
const updatedAt = args.now ?? Date.now();
|
|
2169
|
+
return {
|
|
2170
|
+
type: args.postType ?? DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE,
|
|
2171
|
+
address: args.sender,
|
|
2172
|
+
...args.ref ? { ref: args.ref } : {},
|
|
2173
|
+
content: {
|
|
2174
|
+
peerId: args.peerId,
|
|
2175
|
+
multiaddrs: filterPublicMultiaddrs(args.multiaddrs),
|
|
2176
|
+
browserMultiaddrs: args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
|
|
2177
|
+
browserDialableOnly: true
|
|
2178
|
+
}) : void 0,
|
|
2179
|
+
profile: args.profile,
|
|
2180
|
+
version: args.version,
|
|
2181
|
+
updatedAt: Math.round(updatedAt)
|
|
2182
|
+
},
|
|
2183
|
+
time: now
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
async function createRelayBootstrapPost(args) {
|
|
2187
|
+
const nowMillis = args.now ?? Date.now();
|
|
2188
|
+
const nowSeconds = nowMillis / 1e3;
|
|
2189
|
+
const itemContent = buildRelayBootstrapPostContent({
|
|
2190
|
+
sender: args.sender,
|
|
2191
|
+
peerId: args.peerId,
|
|
2192
|
+
multiaddrs: args.multiaddrs,
|
|
2193
|
+
browserMultiaddrs: args.browserMultiaddrs,
|
|
2194
|
+
ref: args.ref ?? DEFAULT_ALEPH_BOOTSTRAP_REF,
|
|
2195
|
+
postType: args.postType ?? DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE,
|
|
2196
|
+
profile: args.profile,
|
|
2197
|
+
version: args.version,
|
|
2198
|
+
now: nowMillis
|
|
2199
|
+
});
|
|
2200
|
+
itemContent.time = nowSeconds;
|
|
2201
|
+
const serialized = JSON.stringify(itemContent);
|
|
2202
|
+
const itemHash = await args.hasher(serialized);
|
|
2203
|
+
return {
|
|
2204
|
+
channel: args.channel ?? DEFAULT_ALEPH_BOOTSTRAP_CHANNEL,
|
|
2205
|
+
sender: args.sender,
|
|
2206
|
+
chain: "ETH",
|
|
2207
|
+
type: "POST",
|
|
2208
|
+
time: nowSeconds,
|
|
2209
|
+
item_type: "inline",
|
|
2210
|
+
item_content: serialized,
|
|
2211
|
+
item_hash: itemHash
|
|
2212
|
+
};
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
// ../core/src/bootstrap-registration.ts
|
|
2216
|
+
async function publishRelayBootstrapRegistration(args) {
|
|
2217
|
+
const unsigned = await createRelayBootstrapPost({
|
|
2218
|
+
sender: args.sender,
|
|
2219
|
+
peerId: args.peerId,
|
|
2220
|
+
multiaddrs: args.multiaddrs,
|
|
2221
|
+
browserMultiaddrs: args.browserMultiaddrs,
|
|
2222
|
+
channel: args.channel,
|
|
2223
|
+
ref: args.ref,
|
|
2224
|
+
postType: args.postType,
|
|
2225
|
+
profile: args.profile,
|
|
2226
|
+
version: args.version,
|
|
2227
|
+
now: args.now,
|
|
2228
|
+
hasher: args.hasher
|
|
2229
|
+
});
|
|
2230
|
+
const content = JSON.parse(unsigned.item_content);
|
|
2231
|
+
if ((content.content?.multiaddrs?.length ?? 0) === 0) {
|
|
2232
|
+
return {
|
|
2233
|
+
status: "skipped",
|
|
2234
|
+
reason: "No public relay multiaddrs remained after filtering.",
|
|
2235
|
+
publishedMultiaddrs: [],
|
|
2236
|
+
publishedBrowserMultiaddrs: content.content?.browserMultiaddrs ?? []
|
|
2237
|
+
};
|
|
2238
|
+
}
|
|
2239
|
+
const message = await signAlephMessage(unsigned, args.signer);
|
|
2240
|
+
const { response, httpStatus } = await broadcastAlephMessage2(message, {
|
|
2241
|
+
apiHost: args.apiHost ?? DEFAULT_ALEPH_API_HOST3,
|
|
2242
|
+
sync: args.sync ?? true,
|
|
2243
|
+
fetch: args.fetch
|
|
2244
|
+
});
|
|
2245
|
+
return {
|
|
2246
|
+
status: "published",
|
|
2247
|
+
itemHash: unsigned.item_hash,
|
|
2248
|
+
message,
|
|
2249
|
+
response,
|
|
2250
|
+
httpStatus,
|
|
2251
|
+
publishedMultiaddrs: content.content?.multiaddrs ?? [],
|
|
2252
|
+
publishedBrowserMultiaddrs: content.content?.browserMultiaddrs ?? []
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
1915
2255
|
|
|
1916
2256
|
// src/shared/events.ts
|
|
1917
2257
|
function createDeploymentProgressEmitter() {
|
|
@@ -1985,6 +2325,16 @@ function defaultState(props = {}) {
|
|
|
1985
2325
|
deploymentProgress: IDLE_DEPLOYMENT_PROGRESS
|
|
1986
2326
|
};
|
|
1987
2327
|
}
|
|
2328
|
+
function isDebugEnabled(props) {
|
|
2329
|
+
if (props.debug) {
|
|
2330
|
+
return true;
|
|
2331
|
+
}
|
|
2332
|
+
try {
|
|
2333
|
+
return globalThis.localStorage?.getItem("LE_SPACE_UI_DEBUG") === "1";
|
|
2334
|
+
} catch {
|
|
2335
|
+
return false;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
1988
2338
|
async function sha256Hex(payload) {
|
|
1989
2339
|
const digest = await globalThis.crypto.subtle.digest(
|
|
1990
2340
|
"SHA-256",
|
|
@@ -2000,6 +2350,12 @@ function mappedPorts(execution) {
|
|
|
2000
2350
|
})
|
|
2001
2351
|
);
|
|
2002
2352
|
}
|
|
2353
|
+
function runtimeMappedPorts(mappedPortsRecord) {
|
|
2354
|
+
return Object.entries(mappedPortsRecord ?? {}).map(([port, mapping]) => ({
|
|
2355
|
+
label: `${port}/${mapping.udp ? "udp" : "tcp"}`,
|
|
2356
|
+
hostPort: mapping.host ?? null
|
|
2357
|
+
}));
|
|
2358
|
+
}
|
|
2003
2359
|
function rootfsHealth(args) {
|
|
2004
2360
|
if (!args.manifestState.valid || !args.manifestState.manifest) {
|
|
2005
2361
|
return {
|
|
@@ -2054,6 +2410,13 @@ async function resolveManifest(args) {
|
|
|
2054
2410
|
if (pasted) return pasted;
|
|
2055
2411
|
return loadRootfsManifest(args.manifestUrl);
|
|
2056
2412
|
}
|
|
2413
|
+
function relayProfileForManifest(manifest) {
|
|
2414
|
+
if (manifest?.profile === "uc-go-peer") return "uc-go-peer";
|
|
2415
|
+
if (manifest?.profile === "orbitdb-relay-pinner") {
|
|
2416
|
+
return "orbitdb-relay-pinner";
|
|
2417
|
+
}
|
|
2418
|
+
return null;
|
|
2419
|
+
}
|
|
2057
2420
|
async function inspectInstanceRuntime(args) {
|
|
2058
2421
|
const details = {
|
|
2059
2422
|
messageStatus: String(
|
|
@@ -2070,8 +2433,18 @@ async function inspectInstanceRuntime(args) {
|
|
|
2070
2433
|
execution: null,
|
|
2071
2434
|
error: null
|
|
2072
2435
|
};
|
|
2073
|
-
|
|
2074
|
-
|
|
2436
|
+
const lookup = {
|
|
2437
|
+
allocationFound: false,
|
|
2438
|
+
allocationSource: null,
|
|
2439
|
+
crnUrl: null,
|
|
2440
|
+
webUrl: null,
|
|
2441
|
+
executionPayloadFound: false,
|
|
2442
|
+
executionLookupBlocked: false,
|
|
2443
|
+
executionLookupRequestUrl: null,
|
|
2444
|
+
executionLookupVersion: null
|
|
2445
|
+
};
|
|
2446
|
+
if (details.messageStatus === "rejected" || details.messageStatus === "removing") {
|
|
2447
|
+
return { details, lookup };
|
|
2075
2448
|
}
|
|
2076
2449
|
try {
|
|
2077
2450
|
const allocation = await args.client.fetchSchedulerAllocation(args.instance.item_hash) ?? (() => {
|
|
@@ -2086,26 +2459,35 @@ async function inspectInstanceRuntime(args) {
|
|
|
2086
2459
|
period: null
|
|
2087
2460
|
} : null;
|
|
2088
2461
|
})();
|
|
2462
|
+
lookup.allocationFound = Boolean(allocation);
|
|
2463
|
+
lookup.allocationSource = allocation?.source ?? null;
|
|
2089
2464
|
details.allocationSource = allocation?.source ?? null;
|
|
2090
2465
|
details.crnUrl = allocation?.crnUrl ?? null;
|
|
2091
2466
|
details.ipv6 = allocation?.vmIpv6 ?? null;
|
|
2092
2467
|
details.webUrl = await args.client.fetch2n6WebAccessUrl(
|
|
2093
2468
|
args.instance.item_hash
|
|
2094
2469
|
);
|
|
2470
|
+
lookup.crnUrl = details.crnUrl;
|
|
2471
|
+
lookup.webUrl = details.webUrl;
|
|
2095
2472
|
if (!allocation?.crnUrl) {
|
|
2096
|
-
return details;
|
|
2473
|
+
return { details, lookup };
|
|
2097
2474
|
}
|
|
2098
2475
|
const executionLookup = await args.client.fetchCrnExecutionMap(
|
|
2099
2476
|
allocation.crnUrl
|
|
2100
2477
|
);
|
|
2478
|
+
lookup.executionLookupBlocked = executionLookup.blocked;
|
|
2479
|
+
lookup.executionLookupRequestUrl = executionLookup.requestUrl ?? null;
|
|
2480
|
+
lookup.executionLookupVersion = executionLookup.version ?? null;
|
|
2101
2481
|
const executionPayload = executionLookup.payload?.[args.instance.item_hash];
|
|
2482
|
+
lookup.executionPayloadFound = Boolean(executionPayload);
|
|
2102
2483
|
if (!executionPayload) {
|
|
2103
|
-
return details;
|
|
2484
|
+
return { details, lookup };
|
|
2104
2485
|
}
|
|
2105
2486
|
const execution = normalizeExecution(executionPayload, allocation.crnUrl);
|
|
2106
2487
|
if (!execution.networking.proxy_url && details.webUrl) {
|
|
2107
2488
|
execution.networking.proxy_url = details.webUrl;
|
|
2108
2489
|
}
|
|
2490
|
+
details.messageStatus = "processed";
|
|
2109
2491
|
details.execution = execution;
|
|
2110
2492
|
details.hostIpv4 = execution.networking.host_ipv4 ?? execution.networking.ipv4 ?? null;
|
|
2111
2493
|
details.ipv6 = execution.networking.ipv6_ip ?? execution.networking.ipv6 ?? details.ipv6;
|
|
@@ -2113,11 +2495,14 @@ async function inspectInstanceRuntime(args) {
|
|
|
2113
2495
|
details.webUrl = execution.networking.proxy_url ?? details.webUrl;
|
|
2114
2496
|
details.mappedPorts = mappedPorts(execution);
|
|
2115
2497
|
details.sshCommand = buildSshCommand(details.hostIpv4, details.mappedPorts);
|
|
2116
|
-
return details;
|
|
2498
|
+
return { details, lookup };
|
|
2117
2499
|
} catch (error) {
|
|
2118
2500
|
return {
|
|
2119
|
-
|
|
2120
|
-
|
|
2501
|
+
details: {
|
|
2502
|
+
...details,
|
|
2503
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2504
|
+
},
|
|
2505
|
+
lookup
|
|
2121
2506
|
};
|
|
2122
2507
|
}
|
|
2123
2508
|
}
|
|
@@ -2226,9 +2611,13 @@ var SponsorRelayController = class {
|
|
|
2226
2611
|
props;
|
|
2227
2612
|
progressEmitter = createDeploymentProgressEmitter();
|
|
2228
2613
|
runtimeCooldownByHash = /* @__PURE__ */ new Map();
|
|
2614
|
+
runtimeDetailsByHash = /* @__PURE__ */ new Map();
|
|
2615
|
+
debugEnabled;
|
|
2616
|
+
lastRuntimeReadyHash = null;
|
|
2229
2617
|
constructor(props = {}) {
|
|
2230
2618
|
this.props = props;
|
|
2231
2619
|
this.state = defaultState(props);
|
|
2620
|
+
this.debugEnabled = isDebugEnabled(props);
|
|
2232
2621
|
this.client = createAlephBrowserClient({
|
|
2233
2622
|
apiHost: props.apiHost,
|
|
2234
2623
|
crnListUrl: props.crnListUrl,
|
|
@@ -2248,10 +2637,24 @@ var SponsorRelayController = class {
|
|
|
2248
2637
|
getState() {
|
|
2249
2638
|
return this.state;
|
|
2250
2639
|
}
|
|
2640
|
+
updateProps(props) {
|
|
2641
|
+
this.props = { ...this.props, ...props };
|
|
2642
|
+
this.debugEnabled = isDebugEnabled(this.props);
|
|
2643
|
+
}
|
|
2251
2644
|
emit() {
|
|
2252
2645
|
const next = this.state;
|
|
2253
2646
|
this.subscribers.forEach((subscriber) => subscriber(next));
|
|
2254
2647
|
}
|
|
2648
|
+
trace(message, data) {
|
|
2649
|
+
if (!this.debugEnabled) {
|
|
2650
|
+
return;
|
|
2651
|
+
}
|
|
2652
|
+
if (data === void 0) {
|
|
2653
|
+
console.debug("[le-space/ui]", message);
|
|
2654
|
+
return;
|
|
2655
|
+
}
|
|
2656
|
+
console.debug("[le-space/ui]", message, data);
|
|
2657
|
+
}
|
|
2255
2658
|
patch(patch) {
|
|
2256
2659
|
this.state = {
|
|
2257
2660
|
...this.state,
|
|
@@ -2268,6 +2671,14 @@ var SponsorRelayController = class {
|
|
|
2268
2671
|
...event,
|
|
2269
2672
|
timestamp: Date.now()
|
|
2270
2673
|
};
|
|
2674
|
+
this.trace(`progress:${nextEvent.stage}`, {
|
|
2675
|
+
label: nextEvent.label,
|
|
2676
|
+
progress: nextEvent.progress,
|
|
2677
|
+
status: nextEvent.status,
|
|
2678
|
+
itemHash: nextEvent.itemHash ?? null,
|
|
2679
|
+
detail: nextEvent.detail ?? null,
|
|
2680
|
+
error: nextEvent.error ?? null
|
|
2681
|
+
});
|
|
2271
2682
|
this.patch({
|
|
2272
2683
|
deploymentProgress: nextEvent,
|
|
2273
2684
|
statusText: event.error ?? event.detail ?? event.label,
|
|
@@ -2275,6 +2686,145 @@ var SponsorRelayController = class {
|
|
|
2275
2686
|
});
|
|
2276
2687
|
this.progressEmitter.emit(nextEvent);
|
|
2277
2688
|
}
|
|
2689
|
+
async configureRelayBootstrapRegistration(args) {
|
|
2690
|
+
const profile = relayProfileForManifest(this.state.manifest);
|
|
2691
|
+
if (!profile) return;
|
|
2692
|
+
if (!this.state.wallet.address) {
|
|
2693
|
+
throw new Error("A connected wallet is required to register relay bootstrap addresses.");
|
|
2694
|
+
}
|
|
2695
|
+
if (!args.runtime.hostIpv4) {
|
|
2696
|
+
throw new Error("Relay runtime did not expose a host IPv4 address.");
|
|
2697
|
+
}
|
|
2698
|
+
const mappedPorts2 = args.runtime.mappedPorts ?? {};
|
|
2699
|
+
const setupPort = mappedPorts2["80"]?.host ?? null;
|
|
2700
|
+
if (!setupPort) {
|
|
2701
|
+
throw new Error("Relay runtime did not expose the temporary setup endpoint.");
|
|
2702
|
+
}
|
|
2703
|
+
this.emitProgress({
|
|
2704
|
+
stage: "deployment-confirmed",
|
|
2705
|
+
label: "Configuring relay",
|
|
2706
|
+
progress: 93,
|
|
2707
|
+
status: "info",
|
|
2708
|
+
itemHash: args.itemHash,
|
|
2709
|
+
detail: "Waiting for the guest setup endpoint before collecting relay metadata."
|
|
2710
|
+
});
|
|
2711
|
+
const setupHealth = await waitForSetupEndpoint({
|
|
2712
|
+
hostIpv4: args.runtime.hostIpv4,
|
|
2713
|
+
setupPort,
|
|
2714
|
+
fetch: (url, init) => fetch(url, init),
|
|
2715
|
+
attempts: 15,
|
|
2716
|
+
delayMs: 4e3,
|
|
2717
|
+
httpTimeoutMs: 1e4
|
|
2718
|
+
});
|
|
2719
|
+
if (!setupHealth.ok) {
|
|
2720
|
+
throw new Error(
|
|
2721
|
+
`Relay setup endpoint did not become reachable at http://${args.runtime.hostIpv4}:${setupPort}/health.`
|
|
2722
|
+
);
|
|
2723
|
+
}
|
|
2724
|
+
this.emitProgress({
|
|
2725
|
+
stage: "deployment-confirmed",
|
|
2726
|
+
label: "Applying relay networking",
|
|
2727
|
+
progress: 94,
|
|
2728
|
+
status: "info",
|
|
2729
|
+
itemHash: args.itemHash,
|
|
2730
|
+
detail: "Publishing the host port mapping into the guest relay configuration."
|
|
2731
|
+
});
|
|
2732
|
+
if (profile === "orbitdb-relay-pinner") {
|
|
2733
|
+
const tcpPort = mappedPorts2["9091"]?.host ?? 0;
|
|
2734
|
+
const wsPort = mappedPorts2["9092"]?.host ?? mappedPorts2["443"]?.host ?? 0;
|
|
2735
|
+
if (!tcpPort || !wsPort) {
|
|
2736
|
+
throw new Error("OrbitDB relay runtime is missing required mapped TCP/WS ports.");
|
|
2737
|
+
}
|
|
2738
|
+
await configureOrbitdbRelaySetup2({
|
|
2739
|
+
hostIpv4: args.runtime.hostIpv4,
|
|
2740
|
+
publicIpv6: args.runtime.ipv6,
|
|
2741
|
+
setupPort,
|
|
2742
|
+
tcpPort,
|
|
2743
|
+
wsPort,
|
|
2744
|
+
proxyUrl: args.runtime.proxyUrl ?? void 0,
|
|
2745
|
+
metricsPort: mappedPorts2["9090"]?.host ?? null,
|
|
2746
|
+
metricsHttpsPort: mappedPorts2["443"]?.host ?? null,
|
|
2747
|
+
webrtcPort: mappedPorts2["9093"]?.host ?? null,
|
|
2748
|
+
quicPort: mappedPorts2["9094"]?.host ?? null,
|
|
2749
|
+
fetch: (url, init) => fetch(url, init),
|
|
2750
|
+
timeoutMs: 18e4
|
|
2751
|
+
});
|
|
2752
|
+
} else {
|
|
2753
|
+
await configureUcGoPeer({
|
|
2754
|
+
hostIpv4: args.runtime.hostIpv4,
|
|
2755
|
+
publicIpv6: args.runtime.ipv6,
|
|
2756
|
+
setupPort,
|
|
2757
|
+
tcpPort: mappedPorts2["9095"]?.host ?? null,
|
|
2758
|
+
wsPort: mappedPorts2["9097"]?.host ?? mappedPorts2["9095"]?.host ?? null,
|
|
2759
|
+
udpPort: mappedPorts2["9095"]?.udp === true ? mappedPorts2["9095"]?.host ?? null : null,
|
|
2760
|
+
quicPort: mappedPorts2["9095"]?.udp === true ? mappedPorts2["9095"]?.host ?? null : null,
|
|
2761
|
+
webrtcPort: mappedPorts2["9095"]?.udp === true ? mappedPorts2["9095"]?.host ?? null : null,
|
|
2762
|
+
proxyUrl: args.runtime.proxyUrl ?? void 0,
|
|
2763
|
+
fetch: (url, init) => fetch(url, init),
|
|
2764
|
+
timeoutMs: 18e4
|
|
2765
|
+
});
|
|
2766
|
+
}
|
|
2767
|
+
this.emitProgress({
|
|
2768
|
+
stage: "publishing-bootstrap",
|
|
2769
|
+
label: "Reading relay multiaddrs",
|
|
2770
|
+
progress: 95,
|
|
2771
|
+
status: "info",
|
|
2772
|
+
itemHash: args.itemHash,
|
|
2773
|
+
detail: "Waiting for the relay to report its final public multiaddrs."
|
|
2774
|
+
});
|
|
2775
|
+
const metadataResult = await fetchUcGoPeerMetadata({
|
|
2776
|
+
hostIpv4: args.runtime.hostIpv4,
|
|
2777
|
+
setupPort,
|
|
2778
|
+
fetch: (url, init) => fetch(url, init),
|
|
2779
|
+
attempts: 80,
|
|
2780
|
+
delayMs: 3e3,
|
|
2781
|
+
timeoutMs: 24e4
|
|
2782
|
+
});
|
|
2783
|
+
const metadata = metadataResult && typeof metadataResult === "object" && metadataResult.metadata && typeof metadataResult.metadata === "object" ? metadataResult.metadata : null;
|
|
2784
|
+
const peerId = typeof metadata?.peer_id === "string" ? metadata.peer_id : null;
|
|
2785
|
+
const probeMultiaddrs = Array.isArray(metadata?.probe_multiaddrs) ? metadata.probe_multiaddrs.filter(
|
|
2786
|
+
(entry) => typeof entry === "string"
|
|
2787
|
+
) : [];
|
|
2788
|
+
const browserBootstrapMultiaddrs = Array.isArray(
|
|
2789
|
+
metadata?.browser_bootstrap_multiaddrs
|
|
2790
|
+
) ? metadata.browser_bootstrap_multiaddrs.filter(
|
|
2791
|
+
(entry) => typeof entry === "string"
|
|
2792
|
+
) : [];
|
|
2793
|
+
if (!peerId || probeMultiaddrs.length === 0) {
|
|
2794
|
+
throw new Error("Relay metadata did not include a peer ID and public multiaddrs.");
|
|
2795
|
+
}
|
|
2796
|
+
this.emitProgress({
|
|
2797
|
+
stage: "publishing-bootstrap",
|
|
2798
|
+
label: "Publishing bootstrap registration",
|
|
2799
|
+
progress: 96,
|
|
2800
|
+
status: "info",
|
|
2801
|
+
itemHash: args.itemHash,
|
|
2802
|
+
detail: "Writing the relay bootstrap registration to Aleph."
|
|
2803
|
+
});
|
|
2804
|
+
const publication = await publishRelayBootstrapRegistration({
|
|
2805
|
+
sender: this.state.wallet.address,
|
|
2806
|
+
signer: personalSign,
|
|
2807
|
+
hasher: sha256Hex,
|
|
2808
|
+
fetch: (url, init) => fetch(url, init).then(async (response) => ({
|
|
2809
|
+
ok: response.ok,
|
|
2810
|
+
status: response.status,
|
|
2811
|
+
json: async () => await response.json()
|
|
2812
|
+
})),
|
|
2813
|
+
apiHost: this.client.apiHost,
|
|
2814
|
+
peerId,
|
|
2815
|
+
multiaddrs: probeMultiaddrs,
|
|
2816
|
+
browserMultiaddrs: browserBootstrapMultiaddrs,
|
|
2817
|
+
profile,
|
|
2818
|
+
version: this.state.manifest?.version,
|
|
2819
|
+
sync: true
|
|
2820
|
+
});
|
|
2821
|
+
this.trace("deploy:bootstrap-registration", {
|
|
2822
|
+
itemHash: args.itemHash,
|
|
2823
|
+
peerId,
|
|
2824
|
+
metadata,
|
|
2825
|
+
publication
|
|
2826
|
+
});
|
|
2827
|
+
}
|
|
2278
2828
|
syncLatestDeploymentProgress(instances) {
|
|
2279
2829
|
const itemHash = this.state.lastDeploymentHash;
|
|
2280
2830
|
if (!itemHash) {
|
|
@@ -2286,6 +2836,9 @@ var SponsorRelayController = class {
|
|
|
2286
2836
|
if (!latest) {
|
|
2287
2837
|
return;
|
|
2288
2838
|
}
|
|
2839
|
+
const currentProgress = this.state.deploymentProgress;
|
|
2840
|
+
const currentProgressIsForLatestHash = currentProgress.itemHash === itemHash || currentProgress.itemHash == null && currentProgress.stage !== "idle" && currentProgress.stage !== "error";
|
|
2841
|
+
const keepTerminalSuccess = this.lastRuntimeReadyHash === itemHash || currentProgressIsForLatestHash && currentProgress.stage === "completed" && currentProgress.status === "success";
|
|
2289
2842
|
const status = latest.details.messageStatus;
|
|
2290
2843
|
if (status === "rejected") {
|
|
2291
2844
|
this.emitProgress({
|
|
@@ -2300,6 +2853,14 @@ var SponsorRelayController = class {
|
|
|
2300
2853
|
return;
|
|
2301
2854
|
}
|
|
2302
2855
|
if (status !== "processed") {
|
|
2856
|
+
if (keepTerminalSuccess) {
|
|
2857
|
+
this.trace("progress:retain-completed", {
|
|
2858
|
+
reason: "latest refresh still reports non-processed status",
|
|
2859
|
+
itemHash,
|
|
2860
|
+
status
|
|
2861
|
+
});
|
|
2862
|
+
return;
|
|
2863
|
+
}
|
|
2303
2864
|
const submittedAtMs = instanceTimestampMs(latest.instance);
|
|
2304
2865
|
const pendingTooLong = submittedAtMs != null && Date.now() - submittedAtMs >= DEPLOYMENT_PENDING_WARNING_MS;
|
|
2305
2866
|
this.emitProgress({
|
|
@@ -2313,6 +2874,13 @@ var SponsorRelayController = class {
|
|
|
2313
2874
|
return;
|
|
2314
2875
|
}
|
|
2315
2876
|
if (!hasUsableRuntime(latest.details)) {
|
|
2877
|
+
if (keepTerminalSuccess) {
|
|
2878
|
+
this.trace("progress:retain-completed", {
|
|
2879
|
+
reason: "latest refresh lacks runtime details after completed state",
|
|
2880
|
+
itemHash
|
|
2881
|
+
});
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2316
2884
|
this.emitProgress({
|
|
2317
2885
|
stage: "deployment-confirmed",
|
|
2318
2886
|
label: "Waiting for runtime",
|
|
@@ -2359,8 +2927,42 @@ var SponsorRelayController = class {
|
|
|
2359
2927
|
Date.now() + STALE_INSTANCE_ALLOCATION_COOLDOWN_MS
|
|
2360
2928
|
);
|
|
2361
2929
|
}
|
|
2930
|
+
rememberRuntimeDetails(itemHash, details) {
|
|
2931
|
+
if (!hasUsableRuntime(details)) {
|
|
2932
|
+
return;
|
|
2933
|
+
}
|
|
2934
|
+
this.runtimeDetailsByHash.set(itemHash, {
|
|
2935
|
+
...details,
|
|
2936
|
+
mappedPorts: [...details.mappedPorts]
|
|
2937
|
+
});
|
|
2938
|
+
}
|
|
2939
|
+
mergeRememberedRuntimeDetails(itemHash, details) {
|
|
2940
|
+
const remembered = this.runtimeDetailsByHash.get(itemHash);
|
|
2941
|
+
if (!remembered) {
|
|
2942
|
+
return details;
|
|
2943
|
+
}
|
|
2944
|
+
if (details.messageStatus === "processed" && hasUsableRuntime(details)) {
|
|
2945
|
+
return details;
|
|
2946
|
+
}
|
|
2947
|
+
this.trace("runtime:retain-known-details", {
|
|
2948
|
+
itemHash,
|
|
2949
|
+
currentStatus: details.messageStatus,
|
|
2950
|
+
rememberedStatus: remembered.messageStatus,
|
|
2951
|
+
currentHasRuntime: hasUsableRuntime(details)
|
|
2952
|
+
});
|
|
2953
|
+
return {
|
|
2954
|
+
...details,
|
|
2955
|
+
...remembered,
|
|
2956
|
+
error: details.error ?? remembered.error
|
|
2957
|
+
};
|
|
2958
|
+
}
|
|
2362
2959
|
async init() {
|
|
2960
|
+
this.trace("init:start", {
|
|
2961
|
+
launcherMode: this.props.launcherMode ?? "floating",
|
|
2962
|
+
manifestUrl: this.state.manifestUrl
|
|
2963
|
+
});
|
|
2363
2964
|
this.stopWalletWatch = watchWallet(() => {
|
|
2965
|
+
this.trace("wallet:changed");
|
|
2364
2966
|
void this.refreshWalletDerivedState();
|
|
2365
2967
|
});
|
|
2366
2968
|
await this.refresh();
|
|
@@ -2372,6 +2974,7 @@ var SponsorRelayController = class {
|
|
|
2372
2974
|
}, RELAY_PING_INTERVAL_MS);
|
|
2373
2975
|
await this.refreshRelayPing();
|
|
2374
2976
|
this.patch({ ready: true });
|
|
2977
|
+
this.trace("init:ready");
|
|
2375
2978
|
}
|
|
2376
2979
|
destroy() {
|
|
2377
2980
|
if (this.refreshTimer) clearInterval(this.refreshTimer);
|
|
@@ -2472,6 +3075,11 @@ var SponsorRelayController = class {
|
|
|
2472
3075
|
}
|
|
2473
3076
|
}
|
|
2474
3077
|
async refresh() {
|
|
3078
|
+
this.trace("refresh:start", {
|
|
3079
|
+
wallet: this.state.wallet.address,
|
|
3080
|
+
manifestUrl: this.state.manifestUrl,
|
|
3081
|
+
showInstances: this.state.showInstances
|
|
3082
|
+
});
|
|
2475
3083
|
this.patch({
|
|
2476
3084
|
busy: { refreshing: true },
|
|
2477
3085
|
errorText: null,
|
|
@@ -2497,29 +3105,58 @@ var SponsorRelayController = class {
|
|
|
2497
3105
|
balance = nextBalance;
|
|
2498
3106
|
instances = await Promise.all(
|
|
2499
3107
|
rawInstances.map(async (instance) => {
|
|
2500
|
-
const
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
3108
|
+
const inspected = this.canSkipRuntimeRefresh(instance) ? {
|
|
3109
|
+
details: {
|
|
3110
|
+
messageStatus: String(
|
|
3111
|
+
instance.status ?? (instance.confirmed ? "processed" : "pending")
|
|
3112
|
+
).toLowerCase(),
|
|
3113
|
+
allocationSource: null,
|
|
3114
|
+
crnUrl: null,
|
|
3115
|
+
hostIpv4: null,
|
|
3116
|
+
ipv6: null,
|
|
3117
|
+
vmIpv4: null,
|
|
3118
|
+
webUrl: null,
|
|
3119
|
+
sshCommand: null,
|
|
3120
|
+
mappedPorts: [],
|
|
3121
|
+
execution: null,
|
|
3122
|
+
error: null
|
|
3123
|
+
},
|
|
3124
|
+
lookup: {
|
|
3125
|
+
allocationFound: false,
|
|
3126
|
+
allocationSource: null,
|
|
3127
|
+
crnUrl: null,
|
|
3128
|
+
webUrl: null,
|
|
3129
|
+
executionPayloadFound: false,
|
|
3130
|
+
executionLookupBlocked: false,
|
|
3131
|
+
executionLookupRequestUrl: null,
|
|
3132
|
+
executionLookupVersion: null
|
|
3133
|
+
}
|
|
2514
3134
|
} : await inspectInstanceRuntime({
|
|
2515
3135
|
client: this.client,
|
|
2516
3136
|
instance,
|
|
2517
3137
|
crns
|
|
2518
3138
|
});
|
|
2519
|
-
|
|
3139
|
+
const details = inspected.details;
|
|
3140
|
+
const mergedDetails = this.mergeRememberedRuntimeDetails(
|
|
3141
|
+
instance.item_hash,
|
|
3142
|
+
details
|
|
3143
|
+
);
|
|
3144
|
+
if (this.debugEnabled && instance.item_hash === this.state.lastDeploymentHash) {
|
|
3145
|
+
this.trace("runtime:inspect-result", {
|
|
3146
|
+
itemHash: instance.item_hash,
|
|
3147
|
+
messageStatus: details.messageStatus,
|
|
3148
|
+
hostIpv4: details.hostIpv4,
|
|
3149
|
+
vmIpv4: details.vmIpv4,
|
|
3150
|
+
mappedPorts: details.mappedPorts,
|
|
3151
|
+
error: details.error,
|
|
3152
|
+
lookup: inspected.lookup
|
|
3153
|
+
});
|
|
3154
|
+
}
|
|
3155
|
+
this.rememberRuntimeDetails(instance.item_hash, mergedDetails);
|
|
3156
|
+
this.noteRuntimeRefreshResult(instance, mergedDetails);
|
|
2520
3157
|
return {
|
|
2521
3158
|
instance,
|
|
2522
|
-
details
|
|
3159
|
+
details: mergedDetails
|
|
2523
3160
|
};
|
|
2524
3161
|
})
|
|
2525
3162
|
);
|
|
@@ -2556,9 +3193,15 @@ var SponsorRelayController = class {
|
|
|
2556
3193
|
busy: { refreshing: false },
|
|
2557
3194
|
statusText: "Relay sponsor data ready"
|
|
2558
3195
|
});
|
|
3196
|
+
this.trace("refresh:success", {
|
|
3197
|
+
manifestValid: manifestState.valid,
|
|
3198
|
+
rootfsVerified,
|
|
3199
|
+
instances: instances.length
|
|
3200
|
+
});
|
|
2559
3201
|
this.recomputePricingSummary();
|
|
2560
3202
|
this.syncLatestDeploymentProgress(instances);
|
|
2561
3203
|
} catch (error) {
|
|
3204
|
+
this.trace("refresh:error", error);
|
|
2562
3205
|
this.patch({
|
|
2563
3206
|
busy: { refreshing: false },
|
|
2564
3207
|
errorText: error instanceof Error ? error.message : String(error),
|
|
@@ -2569,8 +3212,17 @@ var SponsorRelayController = class {
|
|
|
2569
3212
|
async refreshRelayPing() {
|
|
2570
3213
|
const relayPing = await pingPeer(this.props.libp2p);
|
|
2571
3214
|
this.patch({ relayPing });
|
|
3215
|
+
this.trace("libp2p:relay-ping", relayPing);
|
|
2572
3216
|
}
|
|
2573
3217
|
async deploy() {
|
|
3218
|
+
this.trace("deploy:start", {
|
|
3219
|
+
wallet: this.state.wallet.address,
|
|
3220
|
+
instanceName: this.state.instanceName,
|
|
3221
|
+
tierId: this.state.tierId,
|
|
3222
|
+
selectedCrn: this.state.selectedCrn?.name ?? null,
|
|
3223
|
+
manifestRootfsItemHash: this.state.manifest?.rootfsItemHash ?? null,
|
|
3224
|
+
requiredPortForwards: this.state.manifest?.requiredPortForwards ?? []
|
|
3225
|
+
});
|
|
2574
3226
|
if (!this.state.wallet.address) {
|
|
2575
3227
|
this.patch({ errorText: "Connect MetaMask before deploying." });
|
|
2576
3228
|
return;
|
|
@@ -2635,6 +3287,8 @@ var SponsorRelayController = class {
|
|
|
2635
3287
|
statusText: `Deployment submitted: ${result.itemHash}`,
|
|
2636
3288
|
lastDeploymentHash: result.itemHash
|
|
2637
3289
|
});
|
|
3290
|
+
this.trace("deploy:broadcasted", result);
|
|
3291
|
+
this.lastRuntimeReadyHash = null;
|
|
2638
3292
|
const inspection = await waitForDeploymentResult2(result.itemHash, {
|
|
2639
3293
|
rootfsRef: this.state.manifest.rootfsItemHash,
|
|
2640
3294
|
apiHost: this.client.apiHost,
|
|
@@ -2680,6 +3334,7 @@ var SponsorRelayController = class {
|
|
|
2680
3334
|
inspection.rejectionReason ?? `Deployment ${result.itemHash} stayed ${inspection.status} on Aleph.`
|
|
2681
3335
|
);
|
|
2682
3336
|
}
|
|
3337
|
+
this.trace("deploy:aleph-processed", inspection);
|
|
2683
3338
|
if ((this.state.manifest.requiredPortForwards?.length ?? 0) > 0) {
|
|
2684
3339
|
this.emitProgress({
|
|
2685
3340
|
stage: "refreshing-instances",
|
|
@@ -2699,8 +3354,18 @@ var SponsorRelayController = class {
|
|
|
2699
3354
|
apiHost: this.client.apiHost,
|
|
2700
3355
|
sync: true
|
|
2701
3356
|
});
|
|
3357
|
+
this.trace("deploy:port-forwards-published", {
|
|
3358
|
+
itemHash: result.itemHash,
|
|
3359
|
+
requiredPortForwards: this.state.manifest.requiredPortForwards
|
|
3360
|
+
});
|
|
2702
3361
|
}
|
|
2703
3362
|
if (this.state.selectedCrn?.address) {
|
|
3363
|
+
this.trace("deploy:notifying-crn", {
|
|
3364
|
+
itemHash: result.itemHash,
|
|
3365
|
+
crnName: this.state.selectedCrn.name,
|
|
3366
|
+
crnHash: this.state.selectedCrn.hash,
|
|
3367
|
+
crnUrl: this.state.selectedCrn.address
|
|
3368
|
+
});
|
|
2704
3369
|
await notifyCrnAllocationWithRetry({
|
|
2705
3370
|
crnUrl: this.state.selectedCrn.address,
|
|
2706
3371
|
itemHash: result.itemHash,
|
|
@@ -2753,6 +3418,25 @@ var SponsorRelayController = class {
|
|
|
2753
3418
|
runtime.diagnostics?.reason ?? "Deployment was processed, but runtime networking never exposed mapped ports."
|
|
2754
3419
|
);
|
|
2755
3420
|
}
|
|
3421
|
+
this.trace("deploy:runtime-ready", runtime);
|
|
3422
|
+
this.lastRuntimeReadyHash = result.itemHash;
|
|
3423
|
+
this.rememberRuntimeDetails(result.itemHash, {
|
|
3424
|
+
messageStatus: "processed",
|
|
3425
|
+
allocationSource: runtime.allocation?.source ?? null,
|
|
3426
|
+
crnUrl: runtime.allocation?.crnUrl ?? runtime.execution?.crnUrl ?? null,
|
|
3427
|
+
hostIpv4: runtime.hostIpv4 ?? null,
|
|
3428
|
+
ipv6: runtime.ipv6 ?? null,
|
|
3429
|
+
vmIpv4: runtime.execution?.networking?.ipv4_ip ?? null,
|
|
3430
|
+
webUrl: runtime.webAccessUrl ?? runtime.proxyUrl ?? runtime.execution?.networking?.proxy_url ?? null,
|
|
3431
|
+
sshCommand: runtime.sshCommand ?? null,
|
|
3432
|
+
mappedPorts: runtime.execution != null ? mappedPorts(runtime.execution) : runtimeMappedPorts(runtime.mappedPorts),
|
|
3433
|
+
execution: runtime.execution ?? null,
|
|
3434
|
+
error: null
|
|
3435
|
+
});
|
|
3436
|
+
await this.configureRelayBootstrapRegistration({
|
|
3437
|
+
itemHash: result.itemHash,
|
|
3438
|
+
runtime
|
|
3439
|
+
});
|
|
2756
3440
|
this.patch({
|
|
2757
3441
|
busy: { deploying: false }
|
|
2758
3442
|
});
|
|
@@ -2766,6 +3450,7 @@ var SponsorRelayController = class {
|
|
|
2766
3450
|
});
|
|
2767
3451
|
await this.refresh();
|
|
2768
3452
|
} catch (error) {
|
|
3453
|
+
this.trace("deploy:error", error);
|
|
2769
3454
|
this.patch({
|
|
2770
3455
|
busy: { deploying: false },
|
|
2771
3456
|
errorText: error instanceof Error ? error.message : String(error),
|
|
@@ -2781,6 +3466,7 @@ var SponsorRelayController = class {
|
|
|
2781
3466
|
}
|
|
2782
3467
|
}
|
|
2783
3468
|
async deleteInstance(instanceHash) {
|
|
3469
|
+
this.trace("delete:start", { instanceHash });
|
|
2784
3470
|
if (!this.state.wallet.address) {
|
|
2785
3471
|
this.patch({ errorText: "Connect MetaMask before deleting instances." });
|
|
2786
3472
|
return;
|
|
@@ -2828,6 +3514,7 @@ var SponsorRelayController = class {
|
|
|
2828
3514
|
detail: "Reloading current deployments."
|
|
2829
3515
|
});
|
|
2830
3516
|
await this.refresh();
|
|
3517
|
+
this.trace("delete:submitted", { instanceHash });
|
|
2831
3518
|
this.emitProgress({
|
|
2832
3519
|
stage: "completed",
|
|
2833
3520
|
label: "Delete completed",
|
|
@@ -2838,6 +3525,7 @@ var SponsorRelayController = class {
|
|
|
2838
3525
|
error: null
|
|
2839
3526
|
});
|
|
2840
3527
|
} catch (error) {
|
|
3528
|
+
this.trace("delete:error", error);
|
|
2841
3529
|
this.patch({
|
|
2842
3530
|
busy: { deletingInstanceHash: null },
|
|
2843
3531
|
errorText: error instanceof Error ? error.message : String(error),
|
|
@@ -2858,8 +3546,8 @@ function useSponsorRelayController(props) {
|
|
|
2858
3546
|
[
|
|
2859
3547
|
props.apiHost,
|
|
2860
3548
|
props.crnListUrl,
|
|
3549
|
+
props.debug,
|
|
2861
3550
|
props.instanceName,
|
|
2862
|
-
props.libp2p,
|
|
2863
3551
|
props.manifestJson,
|
|
2864
3552
|
props.manifestUrl,
|
|
2865
3553
|
props.openByDefault,
|
|
@@ -2869,6 +3557,12 @@ function useSponsorRelayController(props) {
|
|
|
2869
3557
|
props.twoN6ApiHost
|
|
2870
3558
|
]
|
|
2871
3559
|
);
|
|
3560
|
+
useEffect(() => {
|
|
3561
|
+
controller.updateProps({
|
|
3562
|
+
libp2p: props.libp2p,
|
|
3563
|
+
debug: props.debug
|
|
3564
|
+
});
|
|
3565
|
+
}, [controller, props.debug, props.libp2p]);
|
|
2872
3566
|
useEffect(() => {
|
|
2873
3567
|
void controller.init();
|
|
2874
3568
|
return () => {
|