Conversation
- Add RemoteHost, PricingItem, UserInfo types + hostIndexUrl option - Add services/hostIndex.ts with fetchHosts, fetchUserInfo, requestPayment (mock data) - Add hosting state to core.ts: connectedHost, userInfo, credit polling, requestTopUp - Add GlobeIcon, CreditIcon, WalletIcon SVG components
- HostBrowser: scrollable host list with cards, manual URL entry, last-used pinning - HostDetail: full host profile with rates table, model chips, connect button - LoggedInDashboard: credit balance, wallet address management, top-up buttons, depleted state
- ConnectionOptions: replace Remote Node section with Browse Remote Hosts button - web.ts: add host-browser, host-detail, logged-in-dashboard view routes - Auth success now navigates to dashboard when a host is selected - Settings button routes to dashboard for remote hosts, pulses red on low credit
…64 mock images - Move getInitials/getHue to utils.ts (shared by HostBrowser + HostDetail) - Replace external image URLs with inline base64 SVG data URIs (COEP-safe) - Make host list responsive: max-height uses min(500px, calc(100vh - 380px)) - Modal uses flex column to pass height constraints to child views - Minor style fixes (shared-styles, ConnectionOptions cleanup)
📝 WalkthroughWalkthroughAdds hosting support: new icon components, host browsing/detail/dashboard LitElements, host-index service with mock data, core hosting state and credit-polling/payment methods, avatar utilities, web UI wiring for host flows, small style tweak, Vite config, and removal of several example pages. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant ConnOpt as ConnectionOptions
participant Browser as HostBrowser
participant Index as HostIndexService
participant Detail as HostDetail
participant Core as Ad4mConnect
participant Dashboard as LoggedInDashboard
User->>ConnOpt: Click "Browse Hosts"
ConnOpt->>Browser: Open host-browser
Browser->>Index: fetchHosts(indexUrl)
Index-->>Browser: return hosts
User->>Browser: select host
Browser->>Detail: Open host-detail(selected)
User->>Detail: Click "Connect"
Detail->>Core: proceedToAuth / validate host
Core->>Index: fetchUserInfo(hostUrl, token)
Index-->>Core: return userInfo
Core->>Core: startCreditPolling()
Core-->>Dashboard: show logged-in-dashboard with host & userInfo
User->>Dashboard: requestTopUp(amount)
Dashboard->>Core: requestTopUp(amount)
Core->>Index: requestPayment(hostUrl, token, amount)
Index-->>Core: payment result
Core-->>Dashboard: emit payment result
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Tip CodeRabbit can approve the review once all CodeRabbit's comments are resolved.Enable the |
There was a problem hiding this comment.
Actionable comments posted: 11
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@connect/src/components/views/ConnectionOptions.ts`:
- Around line 11-12: In ConnectionOptions, the current conditional rendering can
produce an empty modal when isMobile is true and showHosting is false; update
the render logic (the conditional branches that decide whether to render the
local-node and remote-node boxes around the local-node / remote-node elements
referenced in the component) so that at least one action is shown: either always
render the remote-node box when isMobile, or add a fallback branch that forces
showHosting to true or renders a primary fallback action/button when both
local-node and remote-node would be skipped; modify the conditions that use the
showHosting property and isMobile flag so the modal cannot render with no
actions.
In `@connect/src/components/views/HostBrowser.ts`:
- Around line 277-295: Host rows are not keyboard-accessible because the
clickable element is a plain div; update the host row rendering in HostBrowser
(the div with class "host-card" that calls selectHost(host)) to be focusable and
operable via keyboard by adding tabindex="0", role="button", appropriate
aria-label/aria-pressed or aria-selected reflecting lastHostId, and attach a
keydown handler that listens for Enter and Space to call this.selectHost(host);
ensure visual focus styling is preserved and keep existing click behavior, and
adjust any related helpers like selectHost(host) or formatRates only if needed
to support being invoked from keyboard events.
- Around line 210-223: In connectManualUrl(), validate the manualUrl before
constructing the RemoteHost: wrap new URL(url) parsing in a try/catch to handle
malformed URLs and return early with user feedback on parse error, and
explicitly check that parsedUrl.protocol is 'ws:' or 'wss:' (reject other
schemes) before creating the synthetic RemoteHost object (id
`manual-${Date.now()}`, name from parsedUrl.hostname, url, etc.); on validation
failure, do not call new URL() or create the RemoteHost and instead surface an
appropriate error state/message to the user.
In `@connect/src/components/views/HostDetail.ts`:
- Around line 134-139: The formatPrice method treats 0 as falling through to
exponential notation; update the private method formatPrice(price: number) to
explicitly handle price === 0 (or price === 0.0) and return a human-friendly
representation (e.g., "0.00") instead of toExponential; keep the existing
branches for other ranges so values >= 0.01 still use toFixed(2) and the
small-value fallbacks remain unchanged.
- Around line 111-113: In HostDetail, replace the non-semantic back control (the
clickable <div> inside the HostDetail component) with a real <button> element
and give it a distinct class (e.g., "back-button") so it becomes
keyboard-focusable and activatable by Enter/Space; update the global CSS rule
that currently targets all buttons ("button { width: 100%; }") to instead target
the connect action only (e.g., ".connect-button { width: 100%; }") and add
specific styles for ".back-button" to remove default button visuals and preserve
the intended look (e.g., unset default padding/border/background, set cursor:
pointer, display/align as needed). Ensure the back button has an accessible name
(text or aria-label) and reuse the existing click handler (onBack or similar) on
the new <button> element.
In `@connect/src/core.ts`:
- Around line 50-54: The code currently JSON.parse's persisted "ad4m-last-host"
into this.connectedHost (type RemoteHost) which can produce a partial object
missing fields like location, rates, and aiModels; instead, change the restore
logic to parse into a dedicated persisted host shape (e.g., PersistedHost) and
rehydrate/merge it with a full RemoteHost before assigning to connectedHost, or
call an existing initializer/constructor that fills defaults; update the restore
block that uses getLocal("ad4m-last-host") and the assignment to
this.connectedHost so it reconstructs a complete RemoteHost (or calls
setConnectedHost with the parsed id/url/name) rather than directly assigning the
partial parsed object.
In `@connect/src/services/hostIndex.ts`:
- Around line 167-168: The current derivation of the REST baseUrl from hostUrl
always maps both ws:// and wss:// to https://; change the logic so it preserves
TLS intent by mapping wss:// -> https:// and ws:// -> http:// before stripping
the trailing /graphql. Update the baseUrl computation that uses hostUrl
(variable name: baseUrl, hostUrl) to choose the scheme based on
hostUrl.startsWith('wss://') vs 'ws://' and then remove /graphql, and make the
same change for the other identical occurrence referenced around the code (the
second spot at lines 187-188) so /api/user-info and /api/request-payment use the
correct http/https scheme.
- Around line 139-145: The module currently forces mock mode by setting USE_MOCK
= true which short‑circuits public APIs like fetchHosts; change this so mocks
are opt‑in by default (set USE_MOCK = false) and drive it from an explicit
runtime/build flag (e.g., process.env.CONNECT_USE_MOCK or a config value)
instead of a hardcoded true value; update all other mock gates in this file (the
additional mock blocks referenced around the other sections) to read the same
runtime flag so dev/test can enable mocks while production uses the real
backend.
In `@connect/src/utils.ts`:
- Around line 72-75: getInitials uses name.trim() for splitting but falls back
to slicing the original name, causing leading spaces or whitespace-only names to
produce incorrect initials; change the function to normalize once (e.g., const
clean = name.trim()), build words from clean, and in the single-word branch
return clean.slice(0,2).toUpperCase(); also handle the empty clean case (return
an empty string or a defined default) so whitespace-only inputs don't produce a
space character.
In `@connect/src/web.ts`:
- Around line 175-179: The handler for 'userinfochange' currently overwrites
this.userInfo with e.detail, making ephemeral local changes (like a saved wallet
address) lost when the next polling event arrives; change the handler in
connect/src/web.ts to merge incoming e.detail into the existing this.userInfo
instead of replacing it (e.g., preserve locally-saved fields such as the wallet
address or a savedAddress flag), or persist the saved wallet to the
authoritative service/core before updating UI so subsequent 'userinfochange'
events include the saved value; update the event listener that assigns
this.userInfo and this.lowCredit to perform a merge/merge-with-precedence (keep
local saved fields) or trigger the core API to store the saved wallet prior to
assigning e.detail.
- Around line 273-275: Don't assign and persist the selected host before
performing reachability and AD4M validation; instead, test the candidate URL
first and only update this.core.url and call setLocal("ad4m-url", ...) after
validation succeeds. Use a temporary variable (e.g., candidateUrl) to hold
host.url, run the existing reachability/AD4M checks against that variable, and
only on success set this.core.url = candidateUrl and setLocal("ad4m-url",
candidateUrl); leave storage untouched on validation failure (also apply this
change to the same logic covering the block referenced at lines 277-292).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c3782fb5-d5df-443a-8200-0517d7af0152
📒 Files selected for processing (14)
connect/src/components/icons/CreditIcon.tsconnect/src/components/icons/GlobeIcon.tsconnect/src/components/icons/WalletIcon.tsconnect/src/components/icons/index.tsconnect/src/components/views/ConnectionOptions.tsconnect/src/components/views/HostBrowser.tsconnect/src/components/views/HostDetail.tsconnect/src/components/views/LoggedInDashboard.tsconnect/src/core.tsconnect/src/services/hostIndex.tsconnect/src/styles/shared-styles.tsconnect/src/types.tsconnect/src/utils.tsconnect/src/web.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
connect/src/core.ts (1)
312-316: Consider persisting the full host object for richer UI on reload.
setConnectedHostonly persistsid,url, andname, so on reload the dashboard shows placeholder values for location, rates, and models until the user re-browses hosts. If localStorage size isn't a concern, persisting the fullRemoteHostwould provide a better UX.💡 Suggested improvement
setConnectedHost(host: RemoteHost): void { this.connectedHost = host; - setLocal('ad4m-last-host', JSON.stringify({ id: host.id, url: host.url, name: host.name })); + setLocal('ad4m-last-host', JSON.stringify(host)); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@connect/src/core.ts` around lines 312 - 316, Persist the full RemoteHost object instead of only id/url/name in setConnectedHost: update the setConnectedHost method to store JSON.stringify(host) via the existing setLocal('ad4m-last-host', ...) call so the entire RemoteHost (including location, rates, models, etc.) is available on reload for richer UI; ensure you still set this.connectedHost = host before persisting and keep the same storage key.connect/src/web.ts (2)
334-340: Wallet address persistence is incomplete.The TODO comment acknowledges that
handleSetWalletAddressonly updates local state. While theuserinfochangehandler preserves this value temporarily, it will be lost on page reload since there's no server-side persistence.Would you like me to help draft a
setWalletAddressmethod incore.tsthat calls the hosting API to persist the wallet address server-side?🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@connect/src/web.ts` around lines 334 - 340, handleSetWalletAddress currently only mutates local this.userInfo so the hotWalletAddress is lost on reload; implement a persistence path by adding a setWalletAddress method in core.ts that calls the hosting API endpoint to persist the address (accepting user id/session and address), and then modify handleSetWalletAddress to call core.setWalletAddress(e.detail.address), await its result, handle errors (log/display), and only update this.userInfo (this.userInfo = { ...this.userInfo, hotWalletAddress: ... }) after a successful API response; keep references to the existing method names (handleSetWalletAddress, userInfo) and add the new core.setWalletAddress function to locate the changes.
344-358: Dead code:connectRemoteNodeis no longer wired.This handler reads
e.detail.remoteUrlwhich was emitted by the oldConnectionOptionscomponent. Since the component no longer dispatchesconnect-remote-nodeevents andrenderViews()doesn't wire this handler, this code is unreachable.🧹 Suggested cleanup
Either remove the method entirely or add a deprecation comment if backward compatibility is planned:
- private async connectRemoteNode(e: CustomEvent) { - // Legacy direct-URL connection (kept for backward compat if needed) - this.core.url = e.detail.remoteUrl; - setLocal("ad4m-url", this.core.url); - - try { - await connectWebSocket(e.detail.remoteUrl); - const isValidAd4mApi = await this.core.isValidAd4mAPI(); - if (!isValidAd4mApi) throw new Error("Server is reachable but doesn't appear to be an AD4M executor"); - - this.currentView = "remote-authentication"; - } catch (error) { - console.error('[Ad4m Connect UI] Remote connection failed:', error); - } - }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@connect/src/web.ts` around lines 344 - 358, The connectRemoteNode method is dead code (no event wiring) so either remove it or mark it clearly deprecated; to fix, delete the private async connectRemoteNode(...) method and any unused imports/references (connectWebSocket, setLocal usage, currentView set, core.url assignment) OR add a clear deprecation comment above the method and restore the event wiring in renderViews() so connect-remote-node events call connectRemoteNode; reference the method name connectRemoteNode, the helper connectWebSocket, setLocal("ad4m-url", ...), core.isValidAd4mAPI(), and currentView = "remote-authentication" when deciding which approach to apply.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@connect/src/components/views/HostBrowser.ts`:
- Around line 275-277: Replace the non-semantic clickable div used for the back
control with a real button element so it can receive keyboard focus and
activation: change the element rendering that currently outputs <div
class="back-button" `@click`=${this.back}> to a <button class="back-button"
`@click`=${this.back}> and keep using the same back handler (this.back) and CSS
class; also add the suggested CSS (button.back-button { all: unset; cursor:
pointer; }) alongside HostBrowser's styles to preserve visual appearance and
pointer behavior.
---
Nitpick comments:
In `@connect/src/core.ts`:
- Around line 312-316: Persist the full RemoteHost object instead of only
id/url/name in setConnectedHost: update the setConnectedHost method to store
JSON.stringify(host) via the existing setLocal('ad4m-last-host', ...) call so
the entire RemoteHost (including location, rates, models, etc.) is available on
reload for richer UI; ensure you still set this.connectedHost = host before
persisting and keep the same storage key.
In `@connect/src/web.ts`:
- Around line 334-340: handleSetWalletAddress currently only mutates local
this.userInfo so the hotWalletAddress is lost on reload; implement a persistence
path by adding a setWalletAddress method in core.ts that calls the hosting API
endpoint to persist the address (accepting user id/session and address), and
then modify handleSetWalletAddress to call
core.setWalletAddress(e.detail.address), await its result, handle errors
(log/display), and only update this.userInfo (this.userInfo = {
...this.userInfo, hotWalletAddress: ... }) after a successful API response; keep
references to the existing method names (handleSetWalletAddress, userInfo) and
add the new core.setWalletAddress function to locate the changes.
- Around line 344-358: The connectRemoteNode method is dead code (no event
wiring) so either remove it or mark it clearly deprecated; to fix, delete the
private async connectRemoteNode(...) method and any unused imports/references
(connectWebSocket, setLocal usage, currentView set, core.url assignment) OR add
a clear deprecation comment above the method and restore the event wiring in
renderViews() so connect-remote-node events call connectRemoteNode; reference
the method name connectRemoteNode, the helper connectWebSocket,
setLocal("ad4m-url", ...), core.isValidAd4mAPI(), and currentView =
"remote-authentication" when deciding which approach to apply.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f431378a-d328-41f3-92b4-11a10dfcf7e5
📒 Files selected for processing (8)
connect/src/components/views/ConnectionOptions.tsconnect/src/components/views/HostBrowser.tsconnect/src/components/views/HostDetail.tsconnect/src/core.tsconnect/src/services/hostIndex.tsconnect/src/types.tsconnect/src/utils.tsconnect/src/web.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- connect/src/services/hostIndex.ts
….html, simplify index
…M Sans font on :host
There was a problem hiding this comment.
🧹 Nitpick comments (4)
connect/src/web.ts (2)
241-268: Extract shared host-fetching logic.
browseHostsandretryFetchHostsduplicate the same try/catch/finally fetch logic. Consider extracting a shared method:♻️ Suggested refactor
+ private async doFetchHosts() { + this.hostsLoading = true; + this.hostsError = null; + + try { + this.hosts = await fetchHosts(this.core.hostIndexUrl); + } catch (error) { + console.error('[Ad4m Connect UI] Failed to fetch hosts:', error); + this.hostsError = error instanceof Error ? error.message : "Failed to load hosts"; + } finally { + this.hostsLoading = false; + } + } + private async browseHosts() { - this.hostsLoading = true; - this.hostsError = null; this.currentView = "host-browser"; - - try { - this.hosts = await fetchHosts(this.core.hostIndexUrl); - } catch (error) { - console.error('[Ad4m Connect UI] Failed to fetch hosts:', error); - this.hostsError = error instanceof Error ? error.message : "Failed to load hosts"; - } finally { - this.hostsLoading = false; - } + await this.doFetchHosts(); } private async retryFetchHosts() { - this.hostsLoading = true; - this.hostsError = null; - - try { - this.hosts = await fetchHosts(this.core.hostIndexUrl); - } catch (error) { - console.error('[Ad4m Connect UI] Failed to fetch hosts:', error); - this.hostsError = error instanceof Error ? error.message : "Failed to load hosts"; - } finally { - this.hostsLoading = false; - } + await this.doFetchHosts(); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@connect/src/web.ts` around lines 241 - 268, browseHosts and retryFetchHosts duplicate identical fetch/try-catch/finally logic; extract that into a single private helper (e.g. fetchAndSetHosts or loadHosts) which accepts options like setCurrentView flag. Move the shared logic that sets hostsLoading, hostsError, awaits fetchHosts(this.core.hostIndexUrl), assigns this.hosts, logs errors and clears hostsLoading into that helper; update browseHosts to set this.currentView = "host-browser" then call the helper, and update retryFetchHosts to simply call the helper. Ensure the helper uses the same error handling (console.error and this.hostsError assignment) and preserves existing state changes.
335-341: Wallet address save is not persisted to the server.The
handleSetWalletAddressonly updatesuserInfolocally. While theuserinfochangehandler preserves this value during polling, it will be lost on page reload. The comment acknowledges this needs a core method implementation.Given this is a WIP PR, consider adding a TODO or tracking issue to implement the server-side persistence.
Would you like me to open an issue to track implementing the wallet address persistence via a core API method?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@connect/src/web.ts` around lines 335 - 341, handleSetWalletAddress currently only mutates the local this.userInfo and will be lost on reload; update it to call the appropriate core/API persistence method (e.g., add a call like this.core.saveWalletAddress or userService.updateWallet to persist e.detail.address) and only update this.userInfo after a successful response, handling and logging errors; if the core API implementation isn't available yet, add a clear TODO comment in handleSetWalletAddress referencing the missing core method and create/link a tracking issue so persistence is implemented later, and ensure the userinfochange polling logic remains compatible with the persisted value.connect/src/core.ts (1)
279-298: Consider adding backoff on repeated polling failures.Currently, if the host becomes unreachable, the polling will continue logging errors every 30 seconds indefinitely. Consider adding a failure counter and pausing/slowing polling after consecutive failures.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@connect/src/core.ts` around lines 279 - 298, The startCreditPolling implementation currently polls indefinitely and logs errors on each failure; modify startCreditPolling to track consecutive failures (e.g., failureCount), implement an exponential backoff or capped backoff by increasing the interval used for setInterval (or switching to a recursive setTimeout) after each failed fetchUserInfo, reset failureCount to 0 on a successful response, cap the maximum backoff (e.g., 5 minutes) and optionally dispatch an event (e.g., 'creditpollingpaused' or 'creditpollingresumed') when you pause/resume; update references to creditPollInterval and stopCreditPolling so clearing and restarting the timer respects the current backoff interval and ensure failures don’t leak timers when stopCreditPolling is called.connect/src/components/shared/avatar.ts (1)
14-24: Imperative DOM manipulation bypasses Lit's reactive rendering.The
onErrorhandler usesdocument.createElementandreplaceWithwhich breaks out of Lit's declarative model. If the component re-renders, Lit won't be aware of the replaced DOM node, potentially causing stale UI or errors.Consider using reactive state instead:
♻️ Suggested approach using state
Instead of a standalone function, this could be a small Lit component or use a directive that tracks load failure via reactive state:
// Alternative: Use a stateful approach with `@state` in the consuming component // or create a small LitElement for the avatar that tracks its own error state import { html, TemplateResult } from "lit"; import { getInitials, getHue } from "../../utils"; import type { RemoteHost } from "../../types"; export function renderHostAvatar( host: RemoteHost, cls: string, failedIds?: Set<string> ): TemplateResult { const hue = getHue(host.id || host.name); const initials = getInitials(host.name); const hostKey = host.id || host.name; // If image previously failed or no URL, render fallback if (!host.profilePicUrl || failedIds?.has(hostKey)) { return html`<div class="${cls} fallback" style="background:hsl(${hue},60%,35%)">${initials}</div>`; } return html`<img class=${cls} src=${host.profilePicUrl} alt="" `@error`=${(e: Event) => { // Dispatch event to parent to track failure and trigger re-render (e.target as HTMLElement).dispatchEvent( new CustomEvent('avatar-error', { detail: hostKey, bubbles: true, composed: true }) ); }} />`; }The consuming component would maintain a
Set<string>of failed host IDs and re-render on error.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@connect/src/components/shared/avatar.ts` around lines 14 - 24, The onError handler in the avatar render uses imperative DOM manipulation (document.createElement and replaceWith) which breaks Lit's reactive model; change it to notify the parent so reactive state drives fallback rendering: in the branch that returns the <img> (where host.profilePicUrl is present) replace the imperative onError logic with an inline `@error` handler that dispatches a CustomEvent (e.g., 'avatar-error') with a hostKey (host.id || host.name) as detail, then update the consuming component to maintain a Set of failedIds and render the fallback div when failedIds has the hostKey or when no profilePicUrl (mirror the suggested pattern instead of using document.createElement/replaceWith).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@connect/src/components/shared/avatar.ts`:
- Around line 14-24: The onError handler in the avatar render uses imperative
DOM manipulation (document.createElement and replaceWith) which breaks Lit's
reactive model; change it to notify the parent so reactive state drives fallback
rendering: in the branch that returns the <img> (where host.profilePicUrl is
present) replace the imperative onError logic with an inline `@error` handler that
dispatches a CustomEvent (e.g., 'avatar-error') with a hostKey (host.id ||
host.name) as detail, then update the consuming component to maintain a Set of
failedIds and render the fallback div when failedIds has the hostKey or when no
profilePicUrl (mirror the suggested pattern instead of using
document.createElement/replaceWith).
In `@connect/src/core.ts`:
- Around line 279-298: The startCreditPolling implementation currently polls
indefinitely and logs errors on each failure; modify startCreditPolling to track
consecutive failures (e.g., failureCount), implement an exponential backoff or
capped backoff by increasing the interval used for setInterval (or switching to
a recursive setTimeout) after each failed fetchUserInfo, reset failureCount to 0
on a successful response, cap the maximum backoff (e.g., 5 minutes) and
optionally dispatch an event (e.g., 'creditpollingpaused' or
'creditpollingresumed') when you pause/resume; update references to
creditPollInterval and stopCreditPolling so clearing and restarting the timer
respects the current backoff interval and ensure failures don’t leak timers when
stopCreditPolling is called.
In `@connect/src/web.ts`:
- Around line 241-268: browseHosts and retryFetchHosts duplicate identical
fetch/try-catch/finally logic; extract that into a single private helper (e.g.
fetchAndSetHosts or loadHosts) which accepts options like setCurrentView flag.
Move the shared logic that sets hostsLoading, hostsError, awaits
fetchHosts(this.core.hostIndexUrl), assigns this.hosts, logs errors and clears
hostsLoading into that helper; update browseHosts to set this.currentView =
"host-browser" then call the helper, and update retryFetchHosts to simply call
the helper. Ensure the helper uses the same error handling (console.error and
this.hostsError assignment) and preserves existing state changes.
- Around line 335-341: handleSetWalletAddress currently only mutates the local
this.userInfo and will be lost on reload; update it to call the appropriate
core/API persistence method (e.g., add a call like this.core.saveWalletAddress
or userService.updateWallet to persist e.detail.address) and only update
this.userInfo after a successful response, handling and logging errors; if the
core API implementation isn't available yet, add a clear TODO comment in
handleSetWalletAddress referencing the missing core method and create/link a
tracking issue so persistence is implemented later, and ensure the
userinfochange polling logic remains compatible with the persisted value.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8a5191b8-a7ff-4662-80c2-604d958b693b
📒 Files selected for processing (5)
connect/src/components/shared/avatar.tsconnect/src/components/views/HostBrowser.tsconnect/src/components/views/HostDetail.tsconnect/src/core.tsconnect/src/web.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- connect/src/components/views/HostBrowser.ts
- connect/src/components/views/HostDetail.ts
Remote Hosting UI in ad4m-connect
Branch:
feat/remote-hosting-in-ad4m-connectBase:
devSummary
Adds a complete remote hosting flow to the ad4m-connect modal, allowing users to browse available remote hosts, view pricing/model details, authenticate, and manage credits — all without leaving the connection dialog.
What Changed
New Views (3 components)
Modified Views
hosting: trueoption). Simplified props.host-browser,host-detail, andlogged-in-dashboard. Added event handlers for the full hosting flow. Settings button routes to dashboard when connected to a host, with a pulsing animation on low credit.Foundation
RemoteHost,PricingItem,UserInfotypes. ExtendedAd4mConnectOptionswithhosting?: booleanandhostIndexUrl?: string.connectedHost,userInfo), credit polling (30s interval),creditdepleted/userinfochangeevents, localStorage persistence of last-used host.fetchHosts(),fetchUserInfo(),requestPayment(). Includes mock data (10 hosts with base64 SVG avatars) behind aUSE_MOCKflag.getInitials()andgetHue()helpers for avatar fallbacks.GlobeIcon,CreditIcon,WalletIconcomponents + barrel export.GraphQL Contract (executor-side)
HostingUserInfoandPaymentRequestResulttypeshostingGetUserInfoquery,hostingRequestPayment/hostingSetWalletAddressmutationsAgentClientArchitecture
Views communicate upward via
CustomEventdispatch. The orchestrator (web.ts) listens and routes.core.tsmanages state and polling.How to Test
ad4m/connect:pnpm buildhosting: trueis passed togetAd4mClient()optionsCommits
213b6b17c8856b1f92d57d5ecdf5506dFiles Changed
19 files —
+1,554 / -125linesHostBrowser.ts,HostDetail.ts,LoggedInDashboard.tshostIndex.tsGlobeIcon.ts,CreditIcon.ts,WalletIcon.ts,icons/index.tsConnectionOptions.ts,web.ts,core.ts,types.ts,utils.ts,shared-styles.tsgraphql_types.rs,mutation_resolvers.rs,query_resolvers.rs,AgentClient.ts,RuntimeResolver.tsNotes
profilePicUrlis emptymin(500px, calc(100vh - 380px))creditdepletedevent are wired but dashboard top-up flow requires real API integrationUSE_MOCK = trueinhostIndex.ts— flip tofalsewhen the real hosting index API is readySummary by CodeRabbit
New Features
Refactor
Style
Chores