Wallet
+not connected
+diff --git a/README.md b/README.md index 21b4be7..b80ecfb 100644 --- a/README.md +++ b/README.md @@ -21,17 +21,27 @@ Launcher never contains private kernel internals. It verifies and installs signe ## Local Harness (Current) -`app/index.html` is a local launcher shell harness for end-to-end API validation: +`app/index.html` now exposes a product-first control surface with advanced harness tooling preserved. -1. Wallet intent + verify -2. Membership quote + confirm -3. Member channel register/poll/ack/support -4. Governance install token/confirm/status -5. Lease heartbeat + offline renew -6. Injected wallet automation for intent signing and membership tx send -7. One-click `Run membership flow` path (intent -> verify -> quote -> tx -> confirm) +Top-level control surface: -Wallet automation shortcuts in the shell: +1. `Connect wallet` +2. `Activate membership` +3. `Refresh status + feed` +4. `Governance status` +5. Wallet/membership/designation/last-sync overview cards +6. Pull-first updates feed + support ticket action + +Advanced integration controls (collapsible): + +1. API/chain connection settings +2. Wallet intent + verify primitives +3. Membership quote + confirm primitives +4. Member channel register/poll primitives +5. Governance install + lease primitives +6. Raw response log for deterministic troubleshooting + +Wallet automation helpers remain available in advanced controls: 1. `Connect wallet` fills address from `window.ethereum`. 2. `Sign intent (EIP-712)` signs the current intent payload and fills `walletSignature`. diff --git a/app/app.js b/app/app.js index e45f47b..7e34f05 100644 --- a/app/app.js +++ b/app/app.js @@ -6,6 +6,7 @@ const state = { eventMap: new Map(), lastIntent: null, lastQuote: null, + channelReady: false, }; function nowISO() { @@ -52,6 +53,25 @@ function logLine(label, payload) { log.textContent = line + log.textContent; } +function setSummary(id, value) { + const el = $(id); + if (!el) return; + el.textContent = value; +} + +function refreshOverview(statusPayload) { + const currentWallet = wallet(); + setSummary("summaryWallet", currentWallet || "not connected"); + if (statusPayload && typeof statusPayload === "object") { + setSummary("summaryMembership", statusPayload.status || "unknown"); + setSummary("summaryDesignation", statusPayload.designation_code || "-"); + setSummary("summaryLastSync", nowISO()); + return; + } + const designation = $("designationCode")?.value?.trim() || "-"; + setSummary("summaryDesignation", designation); +} + function setFlowStatus(message) { const el = $("flowStatus"); if (el) { @@ -211,6 +231,7 @@ async function onConnectWallet() { provider_chain_id: providerChainID, }); } + refreshOverview(); } async function onSignIntent() { @@ -317,6 +338,7 @@ async function onIntent() { $("designationCode").value = out.designation_code || ""; $("displayToken").value = out.display_token || ""; logLine("wallet intent", out); + refreshOverview(); } async function onVerify() { @@ -330,6 +352,7 @@ async function onVerify() { $("designationCode").value = out.designation_code; } logLine("wallet verify", out); + refreshOverview(); } async function onStatus() { @@ -341,6 +364,7 @@ async function onStatus() { $("designationCode").value = out.designation_code; } logLine("membership status", out); + refreshOverview(out); return out; } @@ -469,7 +493,9 @@ async function onRegisterChannel() { app_version: $("appVersion").value.trim(), push_provider: "none", }); + state.channelReady = true; logLine("channel register", out); + return out; } async function onUnregisterChannel() { @@ -477,7 +503,9 @@ async function onUnregisterChannel() { wallet: requireWallet(), device_id: deviceID(), }); + state.channelReady = false; logLine("channel unregister", out); + return out; } async function onPollEvents() { @@ -497,6 +525,7 @@ async function onPollEvents() { } renderEvents(out.events || []); logLine("channel poll", out); + return out; } async function onSupportTicket() { @@ -552,6 +581,53 @@ async function onInstallStatus() { }); const out = await request("GET", `/governance/install/status?${query.toString()}`); logLine("install status", out); + return out; +} + +async function ensureChannelBinding() { + if (state.channelReady) { + return; + } + await onRegisterChannel(); +} + +async function onQuickConnect() { + await onConnectWallet(); + try { + await onStatus(); + } catch (err) { + logLine("quick connect status warning", { error: String(err) }); + } +} + +async function onQuickActivate() { + await onRunMembershipFlow(); + try { + await ensureChannelBinding(); + await onPollEvents(); + } catch (err) { + logLine("quick activate feed warning", { error: String(err) }); + } +} + +async function onQuickRefresh() { + const status = await onStatus(); + if (String(status.status || "").toLowerCase() !== "active") { + setFlowStatus("membership inactive"); + return; + } + try { + await ensureChannelBinding(); + await onPollEvents(); + setFlowStatus("status synced"); + } catch (err) { + setFlowStatus("feed sync warning"); + throw err; + } +} + +async function onQuickInstallStatus() { + await onInstallStatus(); } async function onLeaseHeartbeat() { @@ -578,7 +654,11 @@ async function onOfflineRenew() { } function bind(id, handler) { - $(id).addEventListener("click", async () => { + const el = $(id); + if (!el) { + return; + } + el.addEventListener("click", async () => { try { await handler(); } catch (err) { @@ -587,6 +667,10 @@ function bind(id, handler) { }); } +bind("btnQuickConnect", onQuickConnect); +bind("btnQuickActivate", onQuickActivate); +bind("btnQuickRefresh", onQuickRefresh); +bind("btnQuickInstallStatus", onQuickInstallStatus); bind("btnConnectWallet", onConnectWallet); bind("btnRunMembershipFlow", onRunMembershipFlow); bind("btnIntent", onIntent); @@ -611,3 +695,4 @@ logLine("launcher shell ready", { api_base: baseURL(), chain_id: chainID(), }); +refreshOverview(); diff --git a/app/index.html b/app/index.html index da189bb..891cb01 100644 --- a/app/index.html +++ b/app/index.html @@ -3,172 +3,53 @@
-Wallet-first onboarding shell (local integration harness)
+Deterministic infrastructure control surface.
ready
not connected
+unknown
+-
+never
+flow idle
-Communication is pull-first. No broadcast notifications.
+