* feat(ui): move Devices page into settings and redesign device inventory - Devices now lives at /settings/devices (System group); /nodes stays as alias - per-device form-factor icons (phone/browser/terminal/machine) with status dot - equal-height header actions; stale cleanup and pairing only - drop manual Refresh button; page already auto-refreshes via presence events + 30s poll - dynamic connected/pending summary replaces static card subtitle * style(ui): oxfmt pass + loading empty state for devices inventory * chore(ui): sync locale bundles for devices settings copy; docs point at Settings → Devices * refactor(ui): split device tile + pending rows out of view-inventory; drop unused icons Keeps the TypeScript LOC ratchet green: view-inventory.ts returns below its baseline and icons.ts sheds unused folderOpen/micOff/volumeOff glyphs. * fix(ui): ratchet LOC baseline, drop unused DeviceIconSource export, settle devices settings copy - baseline: icons.ts 693, view-inventory.ts back under the 500 ceiling - subtitles.nodes now describes the settings page; nav tests updated - locale bundles resynced (fallbacks=0) * refactor(ui): rename token row param for reviewer-tool clarity * fix(ui): collapse device rows in narrow containers and classify the TUI as a terminal client Review findings: the nodes-entry grid override beat the shared 560px list-item collapse, overflowing phone-width rows; openclaw-tui connects with mode ui so only its client id marks it as a terminal. * chore(ui): translate pending locale keys after rebase (fallbacks=0)
5.3 KiB
summary, read_when, title
| summary | read_when | title | |||
|---|---|---|---|---|---|
| How OpenClaw presence entries are produced, merged, and displayed |
|
Presence |
OpenClaw "presence" is a lightweight, best-effort view of:
- the Gateway itself, and
- user-visible clients connected to the Gateway (mac app, WebChat, nodes, etc.)
Presence renders live connection metadata in the Control UI Devices page (under Settings → Devices) and the macOS app's Instances tab.
This page covers the Gateway client roster. To detect the Mac you most recently used and route node alerts there, see Active computer presence.
Presence fields (what shows up)
Presence entries are structured objects with fields like:
instanceId(optional but strongly recommended): stable client identity (usuallyconnect.client.instanceId)host: human-friendly host nameip: best-effort IP addressversion: client version stringdeviceFamily/modelIdentifier: hardware hintsmode:ui,webchat,cli,backend,node,probe,testlastInputSeconds: seconds since last user input, if knownreason: free-form client-supplied string; the Gateway itself only emitsself,connect, anddisconnectdeviceId,roles,scopes: device identity and role/scope hints from the connect handshakets: last update timestamp (ms since epoch)
Producers (where presence comes from)
Presence entries are produced by multiple sources and merged.
1) Gateway self entry
The Gateway always seeds a "self" entry at startup so UIs show the gateway host even before any clients connect.
2) WebSocket connect
Every WS client begins with a connect request. On successful handshake the
Gateway upserts a presence entry for that connection.
Why ephemeral control-plane connections do not show up
CLI commands, backend RPC clients, and probes often connect briefly. To avoid
retaining that churn for the full presence TTL, clients in cli, backend,
or probe mode are not turned into presence entries. Test-mode clients
stay tracked because test suites use them as stand-ins for real clients.
3) system-event beacons
Clients can send richer periodic beacons via the system-event method. The mac
app uses this to report host name, IP, and lastInputSeconds.
4) Node connects (role: node)
When a node connects over the Gateway WebSocket with role: node, the Gateway
upserts a presence entry for that node (same flow as other WS clients).
Merge + dedupe rules (why instanceId matters)
Presence entries are stored in a single in-memory map, keyed case-insensitively
by the first available of, in order: a paired device id, connect.client.instanceId,
or the per-connection id as a last resort.
Ephemeral control-plane clients are excluded from tracking entirely (see
above), so their connection ids never become keys. For every other client, the
connection id fallback means a client that reconnects without a stable
instanceId shows up as a duplicate row.
TTL and bounded size
Presence is intentionally ephemeral:
- TTL: entries older than 5 minutes are pruned
- Max entries: 200 (oldest dropped first)
This keeps the list fresh and avoids unbounded memory growth.
Remote/tunnel caveat (loopback IPs)
When a client connects over an SSH tunnel / local port forward, the Gateway
may see the remote address as 127.0.0.1. To avoid recording that tunnel
address as the client's IP, connect handling omits ip entirely for
detected-local (loopback) clients rather than writing the loopback address
into the entry.
Consumers
Control UI Devices page
The Devices page joins system-presence with durable pairing and node
records. It pins the Gateway self beacon first and uses matching device or
instance ids for live platform, version, model, and input-recency metadata.
macOS Instances tab
The macOS app renders the output of system-presence and applies a small status
indicator (Active/Idle/Stale) based on the age of the last update.
Debugging tips
- To see the raw list, call
system-presenceagainst the Gateway. - If you see duplicates:
- confirm clients send a stable
client.instanceIdin the handshake - confirm periodic beacons use the same
instanceId - check whether the connection-derived entry is missing
instanceId(duplicates are expected)
- confirm clients send a stable