@le-space/ui 0.1.62 → 0.2.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 +8 -0
- package/react/index.js +228 -24
- package/shared/index.d.ts +8 -0
- package/shared/index.js +221 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@le-space/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Floating Aleph sponsor relay UI components for Svelte and React.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"access": "public"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@le-space/browser": "0.
|
|
40
|
-
"@le-space/core": "0.
|
|
39
|
+
"@le-space/browser": "0.2.0",
|
|
40
|
+
"@le-space/core": "0.2.0",
|
|
41
41
|
"viem": "^2.38.5"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
package/react/index.d.ts
CHANGED
|
@@ -190,6 +190,7 @@ type DeploymentProgressListener = (event: DeploymentProgressEvent) => void;
|
|
|
190
190
|
type SponsorRelayHealthTone = 'ok' | 'caution' | 'error' | 'idle';
|
|
191
191
|
interface SponsorRelayProps {
|
|
192
192
|
libp2p?: unknown;
|
|
193
|
+
debug?: boolean;
|
|
193
194
|
manifestUrl?: string;
|
|
194
195
|
manifestJson?: string;
|
|
195
196
|
sshPublicKey?: string;
|
|
@@ -297,16 +298,23 @@ declare class SponsorRelayController {
|
|
|
297
298
|
private props;
|
|
298
299
|
private progressEmitter;
|
|
299
300
|
private runtimeCooldownByHash;
|
|
301
|
+
private runtimeDetailsByHash;
|
|
302
|
+
private debugEnabled;
|
|
303
|
+
private lastRuntimeReadyHash;
|
|
300
304
|
constructor(props?: SponsorRelayProps);
|
|
301
305
|
subscribeToDeploymentProgress(listener: DeploymentProgressListener): () => void;
|
|
302
306
|
subscribe(subscriber: SponsorRelaySubscriber): () => void;
|
|
303
307
|
getState(): SponsorRelayState;
|
|
308
|
+
updateProps(props: Partial<SponsorRelayProps>): void;
|
|
304
309
|
private emit;
|
|
310
|
+
private trace;
|
|
305
311
|
private patch;
|
|
306
312
|
private emitProgress;
|
|
307
313
|
private syncLatestDeploymentProgress;
|
|
308
314
|
private canSkipRuntimeRefresh;
|
|
309
315
|
private noteRuntimeRefreshResult;
|
|
316
|
+
private rememberRuntimeDetails;
|
|
317
|
+
private mergeRememberedRuntimeDetails;
|
|
310
318
|
init(): Promise<void>;
|
|
311
319
|
destroy(): void;
|
|
312
320
|
setOpen(open: boolean): void;
|
package/react/index.js
CHANGED
|
@@ -1985,6 +1985,16 @@ function defaultState(props = {}) {
|
|
|
1985
1985
|
deploymentProgress: IDLE_DEPLOYMENT_PROGRESS
|
|
1986
1986
|
};
|
|
1987
1987
|
}
|
|
1988
|
+
function isDebugEnabled(props) {
|
|
1989
|
+
if (props.debug) {
|
|
1990
|
+
return true;
|
|
1991
|
+
}
|
|
1992
|
+
try {
|
|
1993
|
+
return globalThis.localStorage?.getItem("LE_SPACE_UI_DEBUG") === "1";
|
|
1994
|
+
} catch {
|
|
1995
|
+
return false;
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1988
1998
|
async function sha256Hex(payload) {
|
|
1989
1999
|
const digest = await globalThis.crypto.subtle.digest(
|
|
1990
2000
|
"SHA-256",
|
|
@@ -2000,6 +2010,12 @@ function mappedPorts(execution) {
|
|
|
2000
2010
|
})
|
|
2001
2011
|
);
|
|
2002
2012
|
}
|
|
2013
|
+
function runtimeMappedPorts(mappedPortsRecord) {
|
|
2014
|
+
return Object.entries(mappedPortsRecord ?? {}).map(([port, mapping]) => ({
|
|
2015
|
+
label: `${port}/${mapping.udp ? "udp" : "tcp"}`,
|
|
2016
|
+
hostPort: mapping.host ?? null
|
|
2017
|
+
}));
|
|
2018
|
+
}
|
|
2003
2019
|
function rootfsHealth(args) {
|
|
2004
2020
|
if (!args.manifestState.valid || !args.manifestState.manifest) {
|
|
2005
2021
|
return {
|
|
@@ -2070,8 +2086,18 @@ async function inspectInstanceRuntime(args) {
|
|
|
2070
2086
|
execution: null,
|
|
2071
2087
|
error: null
|
|
2072
2088
|
};
|
|
2073
|
-
|
|
2074
|
-
|
|
2089
|
+
const lookup = {
|
|
2090
|
+
allocationFound: false,
|
|
2091
|
+
allocationSource: null,
|
|
2092
|
+
crnUrl: null,
|
|
2093
|
+
webUrl: null,
|
|
2094
|
+
executionPayloadFound: false,
|
|
2095
|
+
executionLookupBlocked: false,
|
|
2096
|
+
executionLookupRequestUrl: null,
|
|
2097
|
+
executionLookupVersion: null
|
|
2098
|
+
};
|
|
2099
|
+
if (details.messageStatus === "rejected" || details.messageStatus === "removing") {
|
|
2100
|
+
return { details, lookup };
|
|
2075
2101
|
}
|
|
2076
2102
|
try {
|
|
2077
2103
|
const allocation = await args.client.fetchSchedulerAllocation(args.instance.item_hash) ?? (() => {
|
|
@@ -2086,26 +2112,35 @@ async function inspectInstanceRuntime(args) {
|
|
|
2086
2112
|
period: null
|
|
2087
2113
|
} : null;
|
|
2088
2114
|
})();
|
|
2115
|
+
lookup.allocationFound = Boolean(allocation);
|
|
2116
|
+
lookup.allocationSource = allocation?.source ?? null;
|
|
2089
2117
|
details.allocationSource = allocation?.source ?? null;
|
|
2090
2118
|
details.crnUrl = allocation?.crnUrl ?? null;
|
|
2091
2119
|
details.ipv6 = allocation?.vmIpv6 ?? null;
|
|
2092
2120
|
details.webUrl = await args.client.fetch2n6WebAccessUrl(
|
|
2093
2121
|
args.instance.item_hash
|
|
2094
2122
|
);
|
|
2123
|
+
lookup.crnUrl = details.crnUrl;
|
|
2124
|
+
lookup.webUrl = details.webUrl;
|
|
2095
2125
|
if (!allocation?.crnUrl) {
|
|
2096
|
-
return details;
|
|
2126
|
+
return { details, lookup };
|
|
2097
2127
|
}
|
|
2098
2128
|
const executionLookup = await args.client.fetchCrnExecutionMap(
|
|
2099
2129
|
allocation.crnUrl
|
|
2100
2130
|
);
|
|
2131
|
+
lookup.executionLookupBlocked = executionLookup.blocked;
|
|
2132
|
+
lookup.executionLookupRequestUrl = executionLookup.requestUrl ?? null;
|
|
2133
|
+
lookup.executionLookupVersion = executionLookup.version ?? null;
|
|
2101
2134
|
const executionPayload = executionLookup.payload?.[args.instance.item_hash];
|
|
2135
|
+
lookup.executionPayloadFound = Boolean(executionPayload);
|
|
2102
2136
|
if (!executionPayload) {
|
|
2103
|
-
return details;
|
|
2137
|
+
return { details, lookup };
|
|
2104
2138
|
}
|
|
2105
2139
|
const execution = normalizeExecution(executionPayload, allocation.crnUrl);
|
|
2106
2140
|
if (!execution.networking.proxy_url && details.webUrl) {
|
|
2107
2141
|
execution.networking.proxy_url = details.webUrl;
|
|
2108
2142
|
}
|
|
2143
|
+
details.messageStatus = "processed";
|
|
2109
2144
|
details.execution = execution;
|
|
2110
2145
|
details.hostIpv4 = execution.networking.host_ipv4 ?? execution.networking.ipv4 ?? null;
|
|
2111
2146
|
details.ipv6 = execution.networking.ipv6_ip ?? execution.networking.ipv6 ?? details.ipv6;
|
|
@@ -2113,11 +2148,14 @@ async function inspectInstanceRuntime(args) {
|
|
|
2113
2148
|
details.webUrl = execution.networking.proxy_url ?? details.webUrl;
|
|
2114
2149
|
details.mappedPorts = mappedPorts(execution);
|
|
2115
2150
|
details.sshCommand = buildSshCommand(details.hostIpv4, details.mappedPorts);
|
|
2116
|
-
return details;
|
|
2151
|
+
return { details, lookup };
|
|
2117
2152
|
} catch (error) {
|
|
2118
2153
|
return {
|
|
2119
|
-
|
|
2120
|
-
|
|
2154
|
+
details: {
|
|
2155
|
+
...details,
|
|
2156
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2157
|
+
},
|
|
2158
|
+
lookup
|
|
2121
2159
|
};
|
|
2122
2160
|
}
|
|
2123
2161
|
}
|
|
@@ -2226,9 +2264,13 @@ var SponsorRelayController = class {
|
|
|
2226
2264
|
props;
|
|
2227
2265
|
progressEmitter = createDeploymentProgressEmitter();
|
|
2228
2266
|
runtimeCooldownByHash = /* @__PURE__ */ new Map();
|
|
2267
|
+
runtimeDetailsByHash = /* @__PURE__ */ new Map();
|
|
2268
|
+
debugEnabled;
|
|
2269
|
+
lastRuntimeReadyHash = null;
|
|
2229
2270
|
constructor(props = {}) {
|
|
2230
2271
|
this.props = props;
|
|
2231
2272
|
this.state = defaultState(props);
|
|
2273
|
+
this.debugEnabled = isDebugEnabled(props);
|
|
2232
2274
|
this.client = createAlephBrowserClient({
|
|
2233
2275
|
apiHost: props.apiHost,
|
|
2234
2276
|
crnListUrl: props.crnListUrl,
|
|
@@ -2248,10 +2290,24 @@ var SponsorRelayController = class {
|
|
|
2248
2290
|
getState() {
|
|
2249
2291
|
return this.state;
|
|
2250
2292
|
}
|
|
2293
|
+
updateProps(props) {
|
|
2294
|
+
this.props = { ...this.props, ...props };
|
|
2295
|
+
this.debugEnabled = isDebugEnabled(this.props);
|
|
2296
|
+
}
|
|
2251
2297
|
emit() {
|
|
2252
2298
|
const next = this.state;
|
|
2253
2299
|
this.subscribers.forEach((subscriber) => subscriber(next));
|
|
2254
2300
|
}
|
|
2301
|
+
trace(message, data) {
|
|
2302
|
+
if (!this.debugEnabled) {
|
|
2303
|
+
return;
|
|
2304
|
+
}
|
|
2305
|
+
if (data === void 0) {
|
|
2306
|
+
console.debug("[le-space/ui]", message);
|
|
2307
|
+
return;
|
|
2308
|
+
}
|
|
2309
|
+
console.debug("[le-space/ui]", message, data);
|
|
2310
|
+
}
|
|
2255
2311
|
patch(patch) {
|
|
2256
2312
|
this.state = {
|
|
2257
2313
|
...this.state,
|
|
@@ -2268,6 +2324,14 @@ var SponsorRelayController = class {
|
|
|
2268
2324
|
...event,
|
|
2269
2325
|
timestamp: Date.now()
|
|
2270
2326
|
};
|
|
2327
|
+
this.trace(`progress:${nextEvent.stage}`, {
|
|
2328
|
+
label: nextEvent.label,
|
|
2329
|
+
progress: nextEvent.progress,
|
|
2330
|
+
status: nextEvent.status,
|
|
2331
|
+
itemHash: nextEvent.itemHash ?? null,
|
|
2332
|
+
detail: nextEvent.detail ?? null,
|
|
2333
|
+
error: nextEvent.error ?? null
|
|
2334
|
+
});
|
|
2271
2335
|
this.patch({
|
|
2272
2336
|
deploymentProgress: nextEvent,
|
|
2273
2337
|
statusText: event.error ?? event.detail ?? event.label,
|
|
@@ -2286,6 +2350,9 @@ var SponsorRelayController = class {
|
|
|
2286
2350
|
if (!latest) {
|
|
2287
2351
|
return;
|
|
2288
2352
|
}
|
|
2353
|
+
const currentProgress = this.state.deploymentProgress;
|
|
2354
|
+
const currentProgressIsForLatestHash = currentProgress.itemHash === itemHash || currentProgress.itemHash == null && currentProgress.stage !== "idle" && currentProgress.stage !== "error";
|
|
2355
|
+
const keepTerminalSuccess = this.lastRuntimeReadyHash === itemHash || currentProgressIsForLatestHash && currentProgress.stage === "completed" && currentProgress.status === "success";
|
|
2289
2356
|
const status = latest.details.messageStatus;
|
|
2290
2357
|
if (status === "rejected") {
|
|
2291
2358
|
this.emitProgress({
|
|
@@ -2300,6 +2367,14 @@ var SponsorRelayController = class {
|
|
|
2300
2367
|
return;
|
|
2301
2368
|
}
|
|
2302
2369
|
if (status !== "processed") {
|
|
2370
|
+
if (keepTerminalSuccess) {
|
|
2371
|
+
this.trace("progress:retain-completed", {
|
|
2372
|
+
reason: "latest refresh still reports non-processed status",
|
|
2373
|
+
itemHash,
|
|
2374
|
+
status
|
|
2375
|
+
});
|
|
2376
|
+
return;
|
|
2377
|
+
}
|
|
2303
2378
|
const submittedAtMs = instanceTimestampMs(latest.instance);
|
|
2304
2379
|
const pendingTooLong = submittedAtMs != null && Date.now() - submittedAtMs >= DEPLOYMENT_PENDING_WARNING_MS;
|
|
2305
2380
|
this.emitProgress({
|
|
@@ -2313,6 +2388,13 @@ var SponsorRelayController = class {
|
|
|
2313
2388
|
return;
|
|
2314
2389
|
}
|
|
2315
2390
|
if (!hasUsableRuntime(latest.details)) {
|
|
2391
|
+
if (keepTerminalSuccess) {
|
|
2392
|
+
this.trace("progress:retain-completed", {
|
|
2393
|
+
reason: "latest refresh lacks runtime details after completed state",
|
|
2394
|
+
itemHash
|
|
2395
|
+
});
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2316
2398
|
this.emitProgress({
|
|
2317
2399
|
stage: "deployment-confirmed",
|
|
2318
2400
|
label: "Waiting for runtime",
|
|
@@ -2359,8 +2441,42 @@ var SponsorRelayController = class {
|
|
|
2359
2441
|
Date.now() + STALE_INSTANCE_ALLOCATION_COOLDOWN_MS
|
|
2360
2442
|
);
|
|
2361
2443
|
}
|
|
2444
|
+
rememberRuntimeDetails(itemHash, details) {
|
|
2445
|
+
if (!hasUsableRuntime(details)) {
|
|
2446
|
+
return;
|
|
2447
|
+
}
|
|
2448
|
+
this.runtimeDetailsByHash.set(itemHash, {
|
|
2449
|
+
...details,
|
|
2450
|
+
mappedPorts: [...details.mappedPorts]
|
|
2451
|
+
});
|
|
2452
|
+
}
|
|
2453
|
+
mergeRememberedRuntimeDetails(itemHash, details) {
|
|
2454
|
+
const remembered = this.runtimeDetailsByHash.get(itemHash);
|
|
2455
|
+
if (!remembered) {
|
|
2456
|
+
return details;
|
|
2457
|
+
}
|
|
2458
|
+
if (details.messageStatus === "processed" && hasUsableRuntime(details)) {
|
|
2459
|
+
return details;
|
|
2460
|
+
}
|
|
2461
|
+
this.trace("runtime:retain-known-details", {
|
|
2462
|
+
itemHash,
|
|
2463
|
+
currentStatus: details.messageStatus,
|
|
2464
|
+
rememberedStatus: remembered.messageStatus,
|
|
2465
|
+
currentHasRuntime: hasUsableRuntime(details)
|
|
2466
|
+
});
|
|
2467
|
+
return {
|
|
2468
|
+
...details,
|
|
2469
|
+
...remembered,
|
|
2470
|
+
error: details.error ?? remembered.error
|
|
2471
|
+
};
|
|
2472
|
+
}
|
|
2362
2473
|
async init() {
|
|
2474
|
+
this.trace("init:start", {
|
|
2475
|
+
launcherMode: this.props.launcherMode ?? "floating",
|
|
2476
|
+
manifestUrl: this.state.manifestUrl
|
|
2477
|
+
});
|
|
2363
2478
|
this.stopWalletWatch = watchWallet(() => {
|
|
2479
|
+
this.trace("wallet:changed");
|
|
2364
2480
|
void this.refreshWalletDerivedState();
|
|
2365
2481
|
});
|
|
2366
2482
|
await this.refresh();
|
|
@@ -2372,6 +2488,7 @@ var SponsorRelayController = class {
|
|
|
2372
2488
|
}, RELAY_PING_INTERVAL_MS);
|
|
2373
2489
|
await this.refreshRelayPing();
|
|
2374
2490
|
this.patch({ ready: true });
|
|
2491
|
+
this.trace("init:ready");
|
|
2375
2492
|
}
|
|
2376
2493
|
destroy() {
|
|
2377
2494
|
if (this.refreshTimer) clearInterval(this.refreshTimer);
|
|
@@ -2472,6 +2589,11 @@ var SponsorRelayController = class {
|
|
|
2472
2589
|
}
|
|
2473
2590
|
}
|
|
2474
2591
|
async refresh() {
|
|
2592
|
+
this.trace("refresh:start", {
|
|
2593
|
+
wallet: this.state.wallet.address,
|
|
2594
|
+
manifestUrl: this.state.manifestUrl,
|
|
2595
|
+
showInstances: this.state.showInstances
|
|
2596
|
+
});
|
|
2475
2597
|
this.patch({
|
|
2476
2598
|
busy: { refreshing: true },
|
|
2477
2599
|
errorText: null,
|
|
@@ -2497,29 +2619,58 @@ var SponsorRelayController = class {
|
|
|
2497
2619
|
balance = nextBalance;
|
|
2498
2620
|
instances = await Promise.all(
|
|
2499
2621
|
rawInstances.map(async (instance) => {
|
|
2500
|
-
const
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2622
|
+
const inspected = this.canSkipRuntimeRefresh(instance) ? {
|
|
2623
|
+
details: {
|
|
2624
|
+
messageStatus: String(
|
|
2625
|
+
instance.status ?? (instance.confirmed ? "processed" : "pending")
|
|
2626
|
+
).toLowerCase(),
|
|
2627
|
+
allocationSource: null,
|
|
2628
|
+
crnUrl: null,
|
|
2629
|
+
hostIpv4: null,
|
|
2630
|
+
ipv6: null,
|
|
2631
|
+
vmIpv4: null,
|
|
2632
|
+
webUrl: null,
|
|
2633
|
+
sshCommand: null,
|
|
2634
|
+
mappedPorts: [],
|
|
2635
|
+
execution: null,
|
|
2636
|
+
error: null
|
|
2637
|
+
},
|
|
2638
|
+
lookup: {
|
|
2639
|
+
allocationFound: false,
|
|
2640
|
+
allocationSource: null,
|
|
2641
|
+
crnUrl: null,
|
|
2642
|
+
webUrl: null,
|
|
2643
|
+
executionPayloadFound: false,
|
|
2644
|
+
executionLookupBlocked: false,
|
|
2645
|
+
executionLookupRequestUrl: null,
|
|
2646
|
+
executionLookupVersion: null
|
|
2647
|
+
}
|
|
2514
2648
|
} : await inspectInstanceRuntime({
|
|
2515
2649
|
client: this.client,
|
|
2516
2650
|
instance,
|
|
2517
2651
|
crns
|
|
2518
2652
|
});
|
|
2519
|
-
|
|
2653
|
+
const details = inspected.details;
|
|
2654
|
+
const mergedDetails = this.mergeRememberedRuntimeDetails(
|
|
2655
|
+
instance.item_hash,
|
|
2656
|
+
details
|
|
2657
|
+
);
|
|
2658
|
+
if (this.debugEnabled && instance.item_hash === this.state.lastDeploymentHash) {
|
|
2659
|
+
this.trace("runtime:inspect-result", {
|
|
2660
|
+
itemHash: instance.item_hash,
|
|
2661
|
+
messageStatus: details.messageStatus,
|
|
2662
|
+
hostIpv4: details.hostIpv4,
|
|
2663
|
+
vmIpv4: details.vmIpv4,
|
|
2664
|
+
mappedPorts: details.mappedPorts,
|
|
2665
|
+
error: details.error,
|
|
2666
|
+
lookup: inspected.lookup
|
|
2667
|
+
});
|
|
2668
|
+
}
|
|
2669
|
+
this.rememberRuntimeDetails(instance.item_hash, mergedDetails);
|
|
2670
|
+
this.noteRuntimeRefreshResult(instance, mergedDetails);
|
|
2520
2671
|
return {
|
|
2521
2672
|
instance,
|
|
2522
|
-
details
|
|
2673
|
+
details: mergedDetails
|
|
2523
2674
|
};
|
|
2524
2675
|
})
|
|
2525
2676
|
);
|
|
@@ -2556,9 +2707,15 @@ var SponsorRelayController = class {
|
|
|
2556
2707
|
busy: { refreshing: false },
|
|
2557
2708
|
statusText: "Relay sponsor data ready"
|
|
2558
2709
|
});
|
|
2710
|
+
this.trace("refresh:success", {
|
|
2711
|
+
manifestValid: manifestState.valid,
|
|
2712
|
+
rootfsVerified,
|
|
2713
|
+
instances: instances.length
|
|
2714
|
+
});
|
|
2559
2715
|
this.recomputePricingSummary();
|
|
2560
2716
|
this.syncLatestDeploymentProgress(instances);
|
|
2561
2717
|
} catch (error) {
|
|
2718
|
+
this.trace("refresh:error", error);
|
|
2562
2719
|
this.patch({
|
|
2563
2720
|
busy: { refreshing: false },
|
|
2564
2721
|
errorText: error instanceof Error ? error.message : String(error),
|
|
@@ -2569,8 +2726,17 @@ var SponsorRelayController = class {
|
|
|
2569
2726
|
async refreshRelayPing() {
|
|
2570
2727
|
const relayPing = await pingPeer(this.props.libp2p);
|
|
2571
2728
|
this.patch({ relayPing });
|
|
2729
|
+
this.trace("libp2p:relay-ping", relayPing);
|
|
2572
2730
|
}
|
|
2573
2731
|
async deploy() {
|
|
2732
|
+
this.trace("deploy:start", {
|
|
2733
|
+
wallet: this.state.wallet.address,
|
|
2734
|
+
instanceName: this.state.instanceName,
|
|
2735
|
+
tierId: this.state.tierId,
|
|
2736
|
+
selectedCrn: this.state.selectedCrn?.name ?? null,
|
|
2737
|
+
manifestRootfsItemHash: this.state.manifest?.rootfsItemHash ?? null,
|
|
2738
|
+
requiredPortForwards: this.state.manifest?.requiredPortForwards ?? []
|
|
2739
|
+
});
|
|
2574
2740
|
if (!this.state.wallet.address) {
|
|
2575
2741
|
this.patch({ errorText: "Connect MetaMask before deploying." });
|
|
2576
2742
|
return;
|
|
@@ -2635,6 +2801,8 @@ var SponsorRelayController = class {
|
|
|
2635
2801
|
statusText: `Deployment submitted: ${result.itemHash}`,
|
|
2636
2802
|
lastDeploymentHash: result.itemHash
|
|
2637
2803
|
});
|
|
2804
|
+
this.trace("deploy:broadcasted", result);
|
|
2805
|
+
this.lastRuntimeReadyHash = null;
|
|
2638
2806
|
const inspection = await waitForDeploymentResult2(result.itemHash, {
|
|
2639
2807
|
rootfsRef: this.state.manifest.rootfsItemHash,
|
|
2640
2808
|
apiHost: this.client.apiHost,
|
|
@@ -2680,6 +2848,7 @@ var SponsorRelayController = class {
|
|
|
2680
2848
|
inspection.rejectionReason ?? `Deployment ${result.itemHash} stayed ${inspection.status} on Aleph.`
|
|
2681
2849
|
);
|
|
2682
2850
|
}
|
|
2851
|
+
this.trace("deploy:aleph-processed", inspection);
|
|
2683
2852
|
if ((this.state.manifest.requiredPortForwards?.length ?? 0) > 0) {
|
|
2684
2853
|
this.emitProgress({
|
|
2685
2854
|
stage: "refreshing-instances",
|
|
@@ -2699,8 +2868,18 @@ var SponsorRelayController = class {
|
|
|
2699
2868
|
apiHost: this.client.apiHost,
|
|
2700
2869
|
sync: true
|
|
2701
2870
|
});
|
|
2871
|
+
this.trace("deploy:port-forwards-published", {
|
|
2872
|
+
itemHash: result.itemHash,
|
|
2873
|
+
requiredPortForwards: this.state.manifest.requiredPortForwards
|
|
2874
|
+
});
|
|
2702
2875
|
}
|
|
2703
2876
|
if (this.state.selectedCrn?.address) {
|
|
2877
|
+
this.trace("deploy:notifying-crn", {
|
|
2878
|
+
itemHash: result.itemHash,
|
|
2879
|
+
crnName: this.state.selectedCrn.name,
|
|
2880
|
+
crnHash: this.state.selectedCrn.hash,
|
|
2881
|
+
crnUrl: this.state.selectedCrn.address
|
|
2882
|
+
});
|
|
2704
2883
|
await notifyCrnAllocationWithRetry({
|
|
2705
2884
|
crnUrl: this.state.selectedCrn.address,
|
|
2706
2885
|
itemHash: result.itemHash,
|
|
@@ -2753,6 +2932,21 @@ var SponsorRelayController = class {
|
|
|
2753
2932
|
runtime.diagnostics?.reason ?? "Deployment was processed, but runtime networking never exposed mapped ports."
|
|
2754
2933
|
);
|
|
2755
2934
|
}
|
|
2935
|
+
this.trace("deploy:runtime-ready", runtime);
|
|
2936
|
+
this.lastRuntimeReadyHash = result.itemHash;
|
|
2937
|
+
this.rememberRuntimeDetails(result.itemHash, {
|
|
2938
|
+
messageStatus: "processed",
|
|
2939
|
+
allocationSource: runtime.allocation?.source ?? null,
|
|
2940
|
+
crnUrl: runtime.allocation?.crnUrl ?? runtime.execution?.crnUrl ?? null,
|
|
2941
|
+
hostIpv4: runtime.hostIpv4 ?? null,
|
|
2942
|
+
ipv6: runtime.ipv6 ?? null,
|
|
2943
|
+
vmIpv4: runtime.execution?.networking?.ipv4_ip ?? null,
|
|
2944
|
+
webUrl: runtime.webAccessUrl ?? runtime.proxyUrl ?? runtime.execution?.networking?.proxy_url ?? null,
|
|
2945
|
+
sshCommand: runtime.sshCommand ?? null,
|
|
2946
|
+
mappedPorts: runtime.execution != null ? mappedPorts(runtime.execution) : runtimeMappedPorts(runtime.mappedPorts),
|
|
2947
|
+
execution: runtime.execution ?? null,
|
|
2948
|
+
error: null
|
|
2949
|
+
});
|
|
2756
2950
|
this.patch({
|
|
2757
2951
|
busy: { deploying: false }
|
|
2758
2952
|
});
|
|
@@ -2766,6 +2960,7 @@ var SponsorRelayController = class {
|
|
|
2766
2960
|
});
|
|
2767
2961
|
await this.refresh();
|
|
2768
2962
|
} catch (error) {
|
|
2963
|
+
this.trace("deploy:error", error);
|
|
2769
2964
|
this.patch({
|
|
2770
2965
|
busy: { deploying: false },
|
|
2771
2966
|
errorText: error instanceof Error ? error.message : String(error),
|
|
@@ -2781,6 +2976,7 @@ var SponsorRelayController = class {
|
|
|
2781
2976
|
}
|
|
2782
2977
|
}
|
|
2783
2978
|
async deleteInstance(instanceHash) {
|
|
2979
|
+
this.trace("delete:start", { instanceHash });
|
|
2784
2980
|
if (!this.state.wallet.address) {
|
|
2785
2981
|
this.patch({ errorText: "Connect MetaMask before deleting instances." });
|
|
2786
2982
|
return;
|
|
@@ -2828,6 +3024,7 @@ var SponsorRelayController = class {
|
|
|
2828
3024
|
detail: "Reloading current deployments."
|
|
2829
3025
|
});
|
|
2830
3026
|
await this.refresh();
|
|
3027
|
+
this.trace("delete:submitted", { instanceHash });
|
|
2831
3028
|
this.emitProgress({
|
|
2832
3029
|
stage: "completed",
|
|
2833
3030
|
label: "Delete completed",
|
|
@@ -2838,6 +3035,7 @@ var SponsorRelayController = class {
|
|
|
2838
3035
|
error: null
|
|
2839
3036
|
});
|
|
2840
3037
|
} catch (error) {
|
|
3038
|
+
this.trace("delete:error", error);
|
|
2841
3039
|
this.patch({
|
|
2842
3040
|
busy: { deletingInstanceHash: null },
|
|
2843
3041
|
errorText: error instanceof Error ? error.message : String(error),
|
|
@@ -2858,8 +3056,8 @@ function useSponsorRelayController(props) {
|
|
|
2858
3056
|
[
|
|
2859
3057
|
props.apiHost,
|
|
2860
3058
|
props.crnListUrl,
|
|
3059
|
+
props.debug,
|
|
2861
3060
|
props.instanceName,
|
|
2862
|
-
props.libp2p,
|
|
2863
3061
|
props.manifestJson,
|
|
2864
3062
|
props.manifestUrl,
|
|
2865
3063
|
props.openByDefault,
|
|
@@ -2869,6 +3067,12 @@ function useSponsorRelayController(props) {
|
|
|
2869
3067
|
props.twoN6ApiHost
|
|
2870
3068
|
]
|
|
2871
3069
|
);
|
|
3070
|
+
useEffect(() => {
|
|
3071
|
+
controller.updateProps({
|
|
3072
|
+
libp2p: props.libp2p,
|
|
3073
|
+
debug: props.debug
|
|
3074
|
+
});
|
|
3075
|
+
}, [controller, props.debug, props.libp2p]);
|
|
2872
3076
|
useEffect(() => {
|
|
2873
3077
|
void controller.init();
|
|
2874
3078
|
return () => {
|
package/shared/index.d.ts
CHANGED
|
@@ -188,6 +188,7 @@ type DeploymentProgressListener = (event: DeploymentProgressEvent) => void;
|
|
|
188
188
|
type SponsorRelayHealthTone = 'ok' | 'caution' | 'error' | 'idle';
|
|
189
189
|
interface SponsorRelayProps {
|
|
190
190
|
libp2p?: unknown;
|
|
191
|
+
debug?: boolean;
|
|
191
192
|
manifestUrl?: string;
|
|
192
193
|
manifestJson?: string;
|
|
193
194
|
sshPublicKey?: string;
|
|
@@ -343,16 +344,23 @@ declare class SponsorRelayController {
|
|
|
343
344
|
private props;
|
|
344
345
|
private progressEmitter;
|
|
345
346
|
private runtimeCooldownByHash;
|
|
347
|
+
private runtimeDetailsByHash;
|
|
348
|
+
private debugEnabled;
|
|
349
|
+
private lastRuntimeReadyHash;
|
|
346
350
|
constructor(props?: SponsorRelayProps);
|
|
347
351
|
subscribeToDeploymentProgress(listener: DeploymentProgressListener): () => void;
|
|
348
352
|
subscribe(subscriber: SponsorRelaySubscriber): () => void;
|
|
349
353
|
getState(): SponsorRelayState;
|
|
354
|
+
updateProps(props: Partial<SponsorRelayProps>): void;
|
|
350
355
|
private emit;
|
|
356
|
+
private trace;
|
|
351
357
|
private patch;
|
|
352
358
|
private emitProgress;
|
|
353
359
|
private syncLatestDeploymentProgress;
|
|
354
360
|
private canSkipRuntimeRefresh;
|
|
355
361
|
private noteRuntimeRefreshResult;
|
|
362
|
+
private rememberRuntimeDetails;
|
|
363
|
+
private mergeRememberedRuntimeDetails;
|
|
356
364
|
init(): Promise<void>;
|
|
357
365
|
destroy(): void;
|
|
358
366
|
setOpen(open: boolean): void;
|
package/shared/index.js
CHANGED
|
@@ -1984,6 +1984,16 @@ function defaultState(props = {}) {
|
|
|
1984
1984
|
deploymentProgress: IDLE_DEPLOYMENT_PROGRESS
|
|
1985
1985
|
};
|
|
1986
1986
|
}
|
|
1987
|
+
function isDebugEnabled(props) {
|
|
1988
|
+
if (props.debug) {
|
|
1989
|
+
return true;
|
|
1990
|
+
}
|
|
1991
|
+
try {
|
|
1992
|
+
return globalThis.localStorage?.getItem("LE_SPACE_UI_DEBUG") === "1";
|
|
1993
|
+
} catch {
|
|
1994
|
+
return false;
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1987
1997
|
async function sha256Hex(payload) {
|
|
1988
1998
|
const digest = await globalThis.crypto.subtle.digest(
|
|
1989
1999
|
"SHA-256",
|
|
@@ -1999,6 +2009,12 @@ function mappedPorts(execution) {
|
|
|
1999
2009
|
})
|
|
2000
2010
|
);
|
|
2001
2011
|
}
|
|
2012
|
+
function runtimeMappedPorts(mappedPortsRecord) {
|
|
2013
|
+
return Object.entries(mappedPortsRecord ?? {}).map(([port, mapping]) => ({
|
|
2014
|
+
label: `${port}/${mapping.udp ? "udp" : "tcp"}`,
|
|
2015
|
+
hostPort: mapping.host ?? null
|
|
2016
|
+
}));
|
|
2017
|
+
}
|
|
2002
2018
|
function rootfsHealth(args) {
|
|
2003
2019
|
if (!args.manifestState.valid || !args.manifestState.manifest) {
|
|
2004
2020
|
return {
|
|
@@ -2069,8 +2085,18 @@ async function inspectInstanceRuntime(args) {
|
|
|
2069
2085
|
execution: null,
|
|
2070
2086
|
error: null
|
|
2071
2087
|
};
|
|
2072
|
-
|
|
2073
|
-
|
|
2088
|
+
const lookup = {
|
|
2089
|
+
allocationFound: false,
|
|
2090
|
+
allocationSource: null,
|
|
2091
|
+
crnUrl: null,
|
|
2092
|
+
webUrl: null,
|
|
2093
|
+
executionPayloadFound: false,
|
|
2094
|
+
executionLookupBlocked: false,
|
|
2095
|
+
executionLookupRequestUrl: null,
|
|
2096
|
+
executionLookupVersion: null
|
|
2097
|
+
};
|
|
2098
|
+
if (details.messageStatus === "rejected" || details.messageStatus === "removing") {
|
|
2099
|
+
return { details, lookup };
|
|
2074
2100
|
}
|
|
2075
2101
|
try {
|
|
2076
2102
|
const allocation = await args.client.fetchSchedulerAllocation(args.instance.item_hash) ?? (() => {
|
|
@@ -2085,26 +2111,35 @@ async function inspectInstanceRuntime(args) {
|
|
|
2085
2111
|
period: null
|
|
2086
2112
|
} : null;
|
|
2087
2113
|
})();
|
|
2114
|
+
lookup.allocationFound = Boolean(allocation);
|
|
2115
|
+
lookup.allocationSource = allocation?.source ?? null;
|
|
2088
2116
|
details.allocationSource = allocation?.source ?? null;
|
|
2089
2117
|
details.crnUrl = allocation?.crnUrl ?? null;
|
|
2090
2118
|
details.ipv6 = allocation?.vmIpv6 ?? null;
|
|
2091
2119
|
details.webUrl = await args.client.fetch2n6WebAccessUrl(
|
|
2092
2120
|
args.instance.item_hash
|
|
2093
2121
|
);
|
|
2122
|
+
lookup.crnUrl = details.crnUrl;
|
|
2123
|
+
lookup.webUrl = details.webUrl;
|
|
2094
2124
|
if (!allocation?.crnUrl) {
|
|
2095
|
-
return details;
|
|
2125
|
+
return { details, lookup };
|
|
2096
2126
|
}
|
|
2097
2127
|
const executionLookup = await args.client.fetchCrnExecutionMap(
|
|
2098
2128
|
allocation.crnUrl
|
|
2099
2129
|
);
|
|
2130
|
+
lookup.executionLookupBlocked = executionLookup.blocked;
|
|
2131
|
+
lookup.executionLookupRequestUrl = executionLookup.requestUrl ?? null;
|
|
2132
|
+
lookup.executionLookupVersion = executionLookup.version ?? null;
|
|
2100
2133
|
const executionPayload = executionLookup.payload?.[args.instance.item_hash];
|
|
2134
|
+
lookup.executionPayloadFound = Boolean(executionPayload);
|
|
2101
2135
|
if (!executionPayload) {
|
|
2102
|
-
return details;
|
|
2136
|
+
return { details, lookup };
|
|
2103
2137
|
}
|
|
2104
2138
|
const execution = normalizeExecution(executionPayload, allocation.crnUrl);
|
|
2105
2139
|
if (!execution.networking.proxy_url && details.webUrl) {
|
|
2106
2140
|
execution.networking.proxy_url = details.webUrl;
|
|
2107
2141
|
}
|
|
2142
|
+
details.messageStatus = "processed";
|
|
2108
2143
|
details.execution = execution;
|
|
2109
2144
|
details.hostIpv4 = execution.networking.host_ipv4 ?? execution.networking.ipv4 ?? null;
|
|
2110
2145
|
details.ipv6 = execution.networking.ipv6_ip ?? execution.networking.ipv6 ?? details.ipv6;
|
|
@@ -2112,11 +2147,14 @@ async function inspectInstanceRuntime(args) {
|
|
|
2112
2147
|
details.webUrl = execution.networking.proxy_url ?? details.webUrl;
|
|
2113
2148
|
details.mappedPorts = mappedPorts(execution);
|
|
2114
2149
|
details.sshCommand = buildSshCommand(details.hostIpv4, details.mappedPorts);
|
|
2115
|
-
return details;
|
|
2150
|
+
return { details, lookup };
|
|
2116
2151
|
} catch (error) {
|
|
2117
2152
|
return {
|
|
2118
|
-
|
|
2119
|
-
|
|
2153
|
+
details: {
|
|
2154
|
+
...details,
|
|
2155
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2156
|
+
},
|
|
2157
|
+
lookup
|
|
2120
2158
|
};
|
|
2121
2159
|
}
|
|
2122
2160
|
}
|
|
@@ -2225,9 +2263,13 @@ var SponsorRelayController = class {
|
|
|
2225
2263
|
props;
|
|
2226
2264
|
progressEmitter = createDeploymentProgressEmitter();
|
|
2227
2265
|
runtimeCooldownByHash = /* @__PURE__ */ new Map();
|
|
2266
|
+
runtimeDetailsByHash = /* @__PURE__ */ new Map();
|
|
2267
|
+
debugEnabled;
|
|
2268
|
+
lastRuntimeReadyHash = null;
|
|
2228
2269
|
constructor(props = {}) {
|
|
2229
2270
|
this.props = props;
|
|
2230
2271
|
this.state = defaultState(props);
|
|
2272
|
+
this.debugEnabled = isDebugEnabled(props);
|
|
2231
2273
|
this.client = createAlephBrowserClient({
|
|
2232
2274
|
apiHost: props.apiHost,
|
|
2233
2275
|
crnListUrl: props.crnListUrl,
|
|
@@ -2247,10 +2289,24 @@ var SponsorRelayController = class {
|
|
|
2247
2289
|
getState() {
|
|
2248
2290
|
return this.state;
|
|
2249
2291
|
}
|
|
2292
|
+
updateProps(props) {
|
|
2293
|
+
this.props = { ...this.props, ...props };
|
|
2294
|
+
this.debugEnabled = isDebugEnabled(this.props);
|
|
2295
|
+
}
|
|
2250
2296
|
emit() {
|
|
2251
2297
|
const next = this.state;
|
|
2252
2298
|
this.subscribers.forEach((subscriber) => subscriber(next));
|
|
2253
2299
|
}
|
|
2300
|
+
trace(message, data) {
|
|
2301
|
+
if (!this.debugEnabled) {
|
|
2302
|
+
return;
|
|
2303
|
+
}
|
|
2304
|
+
if (data === void 0) {
|
|
2305
|
+
console.debug("[le-space/ui]", message);
|
|
2306
|
+
return;
|
|
2307
|
+
}
|
|
2308
|
+
console.debug("[le-space/ui]", message, data);
|
|
2309
|
+
}
|
|
2254
2310
|
patch(patch) {
|
|
2255
2311
|
this.state = {
|
|
2256
2312
|
...this.state,
|
|
@@ -2267,6 +2323,14 @@ var SponsorRelayController = class {
|
|
|
2267
2323
|
...event,
|
|
2268
2324
|
timestamp: Date.now()
|
|
2269
2325
|
};
|
|
2326
|
+
this.trace(`progress:${nextEvent.stage}`, {
|
|
2327
|
+
label: nextEvent.label,
|
|
2328
|
+
progress: nextEvent.progress,
|
|
2329
|
+
status: nextEvent.status,
|
|
2330
|
+
itemHash: nextEvent.itemHash ?? null,
|
|
2331
|
+
detail: nextEvent.detail ?? null,
|
|
2332
|
+
error: nextEvent.error ?? null
|
|
2333
|
+
});
|
|
2270
2334
|
this.patch({
|
|
2271
2335
|
deploymentProgress: nextEvent,
|
|
2272
2336
|
statusText: event.error ?? event.detail ?? event.label,
|
|
@@ -2285,6 +2349,9 @@ var SponsorRelayController = class {
|
|
|
2285
2349
|
if (!latest) {
|
|
2286
2350
|
return;
|
|
2287
2351
|
}
|
|
2352
|
+
const currentProgress = this.state.deploymentProgress;
|
|
2353
|
+
const currentProgressIsForLatestHash = currentProgress.itemHash === itemHash || currentProgress.itemHash == null && currentProgress.stage !== "idle" && currentProgress.stage !== "error";
|
|
2354
|
+
const keepTerminalSuccess = this.lastRuntimeReadyHash === itemHash || currentProgressIsForLatestHash && currentProgress.stage === "completed" && currentProgress.status === "success";
|
|
2288
2355
|
const status = latest.details.messageStatus;
|
|
2289
2356
|
if (status === "rejected") {
|
|
2290
2357
|
this.emitProgress({
|
|
@@ -2299,6 +2366,14 @@ var SponsorRelayController = class {
|
|
|
2299
2366
|
return;
|
|
2300
2367
|
}
|
|
2301
2368
|
if (status !== "processed") {
|
|
2369
|
+
if (keepTerminalSuccess) {
|
|
2370
|
+
this.trace("progress:retain-completed", {
|
|
2371
|
+
reason: "latest refresh still reports non-processed status",
|
|
2372
|
+
itemHash,
|
|
2373
|
+
status
|
|
2374
|
+
});
|
|
2375
|
+
return;
|
|
2376
|
+
}
|
|
2302
2377
|
const submittedAtMs = instanceTimestampMs(latest.instance);
|
|
2303
2378
|
const pendingTooLong = submittedAtMs != null && Date.now() - submittedAtMs >= DEPLOYMENT_PENDING_WARNING_MS;
|
|
2304
2379
|
this.emitProgress({
|
|
@@ -2312,6 +2387,13 @@ var SponsorRelayController = class {
|
|
|
2312
2387
|
return;
|
|
2313
2388
|
}
|
|
2314
2389
|
if (!hasUsableRuntime(latest.details)) {
|
|
2390
|
+
if (keepTerminalSuccess) {
|
|
2391
|
+
this.trace("progress:retain-completed", {
|
|
2392
|
+
reason: "latest refresh lacks runtime details after completed state",
|
|
2393
|
+
itemHash
|
|
2394
|
+
});
|
|
2395
|
+
return;
|
|
2396
|
+
}
|
|
2315
2397
|
this.emitProgress({
|
|
2316
2398
|
stage: "deployment-confirmed",
|
|
2317
2399
|
label: "Waiting for runtime",
|
|
@@ -2358,8 +2440,42 @@ var SponsorRelayController = class {
|
|
|
2358
2440
|
Date.now() + STALE_INSTANCE_ALLOCATION_COOLDOWN_MS
|
|
2359
2441
|
);
|
|
2360
2442
|
}
|
|
2443
|
+
rememberRuntimeDetails(itemHash, details) {
|
|
2444
|
+
if (!hasUsableRuntime(details)) {
|
|
2445
|
+
return;
|
|
2446
|
+
}
|
|
2447
|
+
this.runtimeDetailsByHash.set(itemHash, {
|
|
2448
|
+
...details,
|
|
2449
|
+
mappedPorts: [...details.mappedPorts]
|
|
2450
|
+
});
|
|
2451
|
+
}
|
|
2452
|
+
mergeRememberedRuntimeDetails(itemHash, details) {
|
|
2453
|
+
const remembered = this.runtimeDetailsByHash.get(itemHash);
|
|
2454
|
+
if (!remembered) {
|
|
2455
|
+
return details;
|
|
2456
|
+
}
|
|
2457
|
+
if (details.messageStatus === "processed" && hasUsableRuntime(details)) {
|
|
2458
|
+
return details;
|
|
2459
|
+
}
|
|
2460
|
+
this.trace("runtime:retain-known-details", {
|
|
2461
|
+
itemHash,
|
|
2462
|
+
currentStatus: details.messageStatus,
|
|
2463
|
+
rememberedStatus: remembered.messageStatus,
|
|
2464
|
+
currentHasRuntime: hasUsableRuntime(details)
|
|
2465
|
+
});
|
|
2466
|
+
return {
|
|
2467
|
+
...details,
|
|
2468
|
+
...remembered,
|
|
2469
|
+
error: details.error ?? remembered.error
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2361
2472
|
async init() {
|
|
2473
|
+
this.trace("init:start", {
|
|
2474
|
+
launcherMode: this.props.launcherMode ?? "floating",
|
|
2475
|
+
manifestUrl: this.state.manifestUrl
|
|
2476
|
+
});
|
|
2362
2477
|
this.stopWalletWatch = watchWallet(() => {
|
|
2478
|
+
this.trace("wallet:changed");
|
|
2363
2479
|
void this.refreshWalletDerivedState();
|
|
2364
2480
|
});
|
|
2365
2481
|
await this.refresh();
|
|
@@ -2371,6 +2487,7 @@ var SponsorRelayController = class {
|
|
|
2371
2487
|
}, RELAY_PING_INTERVAL_MS);
|
|
2372
2488
|
await this.refreshRelayPing();
|
|
2373
2489
|
this.patch({ ready: true });
|
|
2490
|
+
this.trace("init:ready");
|
|
2374
2491
|
}
|
|
2375
2492
|
destroy() {
|
|
2376
2493
|
if (this.refreshTimer) clearInterval(this.refreshTimer);
|
|
@@ -2471,6 +2588,11 @@ var SponsorRelayController = class {
|
|
|
2471
2588
|
}
|
|
2472
2589
|
}
|
|
2473
2590
|
async refresh() {
|
|
2591
|
+
this.trace("refresh:start", {
|
|
2592
|
+
wallet: this.state.wallet.address,
|
|
2593
|
+
manifestUrl: this.state.manifestUrl,
|
|
2594
|
+
showInstances: this.state.showInstances
|
|
2595
|
+
});
|
|
2474
2596
|
this.patch({
|
|
2475
2597
|
busy: { refreshing: true },
|
|
2476
2598
|
errorText: null,
|
|
@@ -2496,29 +2618,58 @@ var SponsorRelayController = class {
|
|
|
2496
2618
|
balance = nextBalance;
|
|
2497
2619
|
instances = await Promise.all(
|
|
2498
2620
|
rawInstances.map(async (instance) => {
|
|
2499
|
-
const
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2621
|
+
const inspected = this.canSkipRuntimeRefresh(instance) ? {
|
|
2622
|
+
details: {
|
|
2623
|
+
messageStatus: String(
|
|
2624
|
+
instance.status ?? (instance.confirmed ? "processed" : "pending")
|
|
2625
|
+
).toLowerCase(),
|
|
2626
|
+
allocationSource: null,
|
|
2627
|
+
crnUrl: null,
|
|
2628
|
+
hostIpv4: null,
|
|
2629
|
+
ipv6: null,
|
|
2630
|
+
vmIpv4: null,
|
|
2631
|
+
webUrl: null,
|
|
2632
|
+
sshCommand: null,
|
|
2633
|
+
mappedPorts: [],
|
|
2634
|
+
execution: null,
|
|
2635
|
+
error: null
|
|
2636
|
+
},
|
|
2637
|
+
lookup: {
|
|
2638
|
+
allocationFound: false,
|
|
2639
|
+
allocationSource: null,
|
|
2640
|
+
crnUrl: null,
|
|
2641
|
+
webUrl: null,
|
|
2642
|
+
executionPayloadFound: false,
|
|
2643
|
+
executionLookupBlocked: false,
|
|
2644
|
+
executionLookupRequestUrl: null,
|
|
2645
|
+
executionLookupVersion: null
|
|
2646
|
+
}
|
|
2513
2647
|
} : await inspectInstanceRuntime({
|
|
2514
2648
|
client: this.client,
|
|
2515
2649
|
instance,
|
|
2516
2650
|
crns
|
|
2517
2651
|
});
|
|
2518
|
-
|
|
2652
|
+
const details = inspected.details;
|
|
2653
|
+
const mergedDetails = this.mergeRememberedRuntimeDetails(
|
|
2654
|
+
instance.item_hash,
|
|
2655
|
+
details
|
|
2656
|
+
);
|
|
2657
|
+
if (this.debugEnabled && instance.item_hash === this.state.lastDeploymentHash) {
|
|
2658
|
+
this.trace("runtime:inspect-result", {
|
|
2659
|
+
itemHash: instance.item_hash,
|
|
2660
|
+
messageStatus: details.messageStatus,
|
|
2661
|
+
hostIpv4: details.hostIpv4,
|
|
2662
|
+
vmIpv4: details.vmIpv4,
|
|
2663
|
+
mappedPorts: details.mappedPorts,
|
|
2664
|
+
error: details.error,
|
|
2665
|
+
lookup: inspected.lookup
|
|
2666
|
+
});
|
|
2667
|
+
}
|
|
2668
|
+
this.rememberRuntimeDetails(instance.item_hash, mergedDetails);
|
|
2669
|
+
this.noteRuntimeRefreshResult(instance, mergedDetails);
|
|
2519
2670
|
return {
|
|
2520
2671
|
instance,
|
|
2521
|
-
details
|
|
2672
|
+
details: mergedDetails
|
|
2522
2673
|
};
|
|
2523
2674
|
})
|
|
2524
2675
|
);
|
|
@@ -2555,9 +2706,15 @@ var SponsorRelayController = class {
|
|
|
2555
2706
|
busy: { refreshing: false },
|
|
2556
2707
|
statusText: "Relay sponsor data ready"
|
|
2557
2708
|
});
|
|
2709
|
+
this.trace("refresh:success", {
|
|
2710
|
+
manifestValid: manifestState.valid,
|
|
2711
|
+
rootfsVerified,
|
|
2712
|
+
instances: instances.length
|
|
2713
|
+
});
|
|
2558
2714
|
this.recomputePricingSummary();
|
|
2559
2715
|
this.syncLatestDeploymentProgress(instances);
|
|
2560
2716
|
} catch (error) {
|
|
2717
|
+
this.trace("refresh:error", error);
|
|
2561
2718
|
this.patch({
|
|
2562
2719
|
busy: { refreshing: false },
|
|
2563
2720
|
errorText: error instanceof Error ? error.message : String(error),
|
|
@@ -2568,8 +2725,17 @@ var SponsorRelayController = class {
|
|
|
2568
2725
|
async refreshRelayPing() {
|
|
2569
2726
|
const relayPing = await pingPeer(this.props.libp2p);
|
|
2570
2727
|
this.patch({ relayPing });
|
|
2728
|
+
this.trace("libp2p:relay-ping", relayPing);
|
|
2571
2729
|
}
|
|
2572
2730
|
async deploy() {
|
|
2731
|
+
this.trace("deploy:start", {
|
|
2732
|
+
wallet: this.state.wallet.address,
|
|
2733
|
+
instanceName: this.state.instanceName,
|
|
2734
|
+
tierId: this.state.tierId,
|
|
2735
|
+
selectedCrn: this.state.selectedCrn?.name ?? null,
|
|
2736
|
+
manifestRootfsItemHash: this.state.manifest?.rootfsItemHash ?? null,
|
|
2737
|
+
requiredPortForwards: this.state.manifest?.requiredPortForwards ?? []
|
|
2738
|
+
});
|
|
2573
2739
|
if (!this.state.wallet.address) {
|
|
2574
2740
|
this.patch({ errorText: "Connect MetaMask before deploying." });
|
|
2575
2741
|
return;
|
|
@@ -2634,6 +2800,8 @@ var SponsorRelayController = class {
|
|
|
2634
2800
|
statusText: `Deployment submitted: ${result.itemHash}`,
|
|
2635
2801
|
lastDeploymentHash: result.itemHash
|
|
2636
2802
|
});
|
|
2803
|
+
this.trace("deploy:broadcasted", result);
|
|
2804
|
+
this.lastRuntimeReadyHash = null;
|
|
2637
2805
|
const inspection = await waitForDeploymentResult2(result.itemHash, {
|
|
2638
2806
|
rootfsRef: this.state.manifest.rootfsItemHash,
|
|
2639
2807
|
apiHost: this.client.apiHost,
|
|
@@ -2679,6 +2847,7 @@ var SponsorRelayController = class {
|
|
|
2679
2847
|
inspection.rejectionReason ?? `Deployment ${result.itemHash} stayed ${inspection.status} on Aleph.`
|
|
2680
2848
|
);
|
|
2681
2849
|
}
|
|
2850
|
+
this.trace("deploy:aleph-processed", inspection);
|
|
2682
2851
|
if ((this.state.manifest.requiredPortForwards?.length ?? 0) > 0) {
|
|
2683
2852
|
this.emitProgress({
|
|
2684
2853
|
stage: "refreshing-instances",
|
|
@@ -2698,8 +2867,18 @@ var SponsorRelayController = class {
|
|
|
2698
2867
|
apiHost: this.client.apiHost,
|
|
2699
2868
|
sync: true
|
|
2700
2869
|
});
|
|
2870
|
+
this.trace("deploy:port-forwards-published", {
|
|
2871
|
+
itemHash: result.itemHash,
|
|
2872
|
+
requiredPortForwards: this.state.manifest.requiredPortForwards
|
|
2873
|
+
});
|
|
2701
2874
|
}
|
|
2702
2875
|
if (this.state.selectedCrn?.address) {
|
|
2876
|
+
this.trace("deploy:notifying-crn", {
|
|
2877
|
+
itemHash: result.itemHash,
|
|
2878
|
+
crnName: this.state.selectedCrn.name,
|
|
2879
|
+
crnHash: this.state.selectedCrn.hash,
|
|
2880
|
+
crnUrl: this.state.selectedCrn.address
|
|
2881
|
+
});
|
|
2703
2882
|
await notifyCrnAllocationWithRetry({
|
|
2704
2883
|
crnUrl: this.state.selectedCrn.address,
|
|
2705
2884
|
itemHash: result.itemHash,
|
|
@@ -2752,6 +2931,21 @@ var SponsorRelayController = class {
|
|
|
2752
2931
|
runtime.diagnostics?.reason ?? "Deployment was processed, but runtime networking never exposed mapped ports."
|
|
2753
2932
|
);
|
|
2754
2933
|
}
|
|
2934
|
+
this.trace("deploy:runtime-ready", runtime);
|
|
2935
|
+
this.lastRuntimeReadyHash = result.itemHash;
|
|
2936
|
+
this.rememberRuntimeDetails(result.itemHash, {
|
|
2937
|
+
messageStatus: "processed",
|
|
2938
|
+
allocationSource: runtime.allocation?.source ?? null,
|
|
2939
|
+
crnUrl: runtime.allocation?.crnUrl ?? runtime.execution?.crnUrl ?? null,
|
|
2940
|
+
hostIpv4: runtime.hostIpv4 ?? null,
|
|
2941
|
+
ipv6: runtime.ipv6 ?? null,
|
|
2942
|
+
vmIpv4: runtime.execution?.networking?.ipv4_ip ?? null,
|
|
2943
|
+
webUrl: runtime.webAccessUrl ?? runtime.proxyUrl ?? runtime.execution?.networking?.proxy_url ?? null,
|
|
2944
|
+
sshCommand: runtime.sshCommand ?? null,
|
|
2945
|
+
mappedPorts: runtime.execution != null ? mappedPorts(runtime.execution) : runtimeMappedPorts(runtime.mappedPorts),
|
|
2946
|
+
execution: runtime.execution ?? null,
|
|
2947
|
+
error: null
|
|
2948
|
+
});
|
|
2755
2949
|
this.patch({
|
|
2756
2950
|
busy: { deploying: false }
|
|
2757
2951
|
});
|
|
@@ -2765,6 +2959,7 @@ var SponsorRelayController = class {
|
|
|
2765
2959
|
});
|
|
2766
2960
|
await this.refresh();
|
|
2767
2961
|
} catch (error) {
|
|
2962
|
+
this.trace("deploy:error", error);
|
|
2768
2963
|
this.patch({
|
|
2769
2964
|
busy: { deploying: false },
|
|
2770
2965
|
errorText: error instanceof Error ? error.message : String(error),
|
|
@@ -2780,6 +2975,7 @@ var SponsorRelayController = class {
|
|
|
2780
2975
|
}
|
|
2781
2976
|
}
|
|
2782
2977
|
async deleteInstance(instanceHash) {
|
|
2978
|
+
this.trace("delete:start", { instanceHash });
|
|
2783
2979
|
if (!this.state.wallet.address) {
|
|
2784
2980
|
this.patch({ errorText: "Connect MetaMask before deleting instances." });
|
|
2785
2981
|
return;
|
|
@@ -2827,6 +3023,7 @@ var SponsorRelayController = class {
|
|
|
2827
3023
|
detail: "Reloading current deployments."
|
|
2828
3024
|
});
|
|
2829
3025
|
await this.refresh();
|
|
3026
|
+
this.trace("delete:submitted", { instanceHash });
|
|
2830
3027
|
this.emitProgress({
|
|
2831
3028
|
stage: "completed",
|
|
2832
3029
|
label: "Delete completed",
|
|
@@ -2837,6 +3034,7 @@ var SponsorRelayController = class {
|
|
|
2837
3034
|
error: null
|
|
2838
3035
|
});
|
|
2839
3036
|
} catch (error) {
|
|
3037
|
+
this.trace("delete:error", error);
|
|
2840
3038
|
this.patch({
|
|
2841
3039
|
busy: { deletingInstanceHash: null },
|
|
2842
3040
|
errorText: error instanceof Error ? error.message : String(error),
|