The Linux desktop companion could complete a CLI install and still claim
'Installation did not finish' with circular update advice, discarding the
real failure. Verified end-to-end in a clean Ubuntu VM across all three
release channels:
- cli.rs: failed CLI commands now surface their stderr tail (deduped, last
12 lines) instead of being mislabeled as JSON parse failures.
- gateway.rs: missing dashboard --json support maps to an honest curated
message pointing at Beta/Development channels, not a circular npm-update
hint.
- main.rs: run 'doctor --fix --non-interactive' right after install so the
CLI repairs config/state before Gateway readiness checks; wrap
post-install failures as 'installed, but connecting failed: <reason>'.
- installer.rs: keep structured step events out of the prose failure tail.
- ui/main.js: humanize streamed install steps, render real errors on the
failure screen, and always offer Reinstall from connection failures.
- scripts/install-cli.sh: service refresh uses 'gateway status --json' with
the bundled node runtime, corepack failure falls back to npm, dev channel
clones with --filter=blob:none.
* feat(linux): suspend the local gateway across host sleep via logind
Mirror the macOS sleep cycle in the Tauri companion: a logind
PrepareForSleep listener with a delay inhibitor best-effort prepares a
local gateway suspension before sleep and resumes it on wake with a
reconnect nudge and bounded retries; leases are route-bound, always
cleared on wake, and late prepare responses resume immediately.
* fix(linux): keep the sleep listener alive when inhibitor re-acquire fails
* fix(linux): activate the driver for sleep cycles and bound driver-side sleep RPCs
Sleep cycles temporarily unpark the gateway driver so prepare/resume work
without the Quick Chat window; suspend RPCs carry a 3s driver-side budget
that recycles a hung socket instead of starving wake retries; a mock-logind
integration test exercises the Linux listener end to end.
* fix(linux): count overlapping sleep cycles instead of a boolean flag
An older wake task ending late cleared the shared activation flag and
parked the driver mid-cycle; a saturating depth counter keeps the driver
active until the last cycle ends.
* fix(linux): gate sleep-cycle activation on loopback routes and never block wake on logind
Remote or unconfigured routes no longer activate the driver during
sleep, the paired end hook runs only for cycles that began, and wake
recovery is spawned before the inhibitor re-acquire so a slow logind
cannot delay reconnect/resume.
* feat(linux): add Quick Chat floating composer to the Tauri companion
- frameless transparent always-on-top quickchat window (on-demand, canvas
pattern), positioned top-third on the cursor monitor, CSS fade+zoom in/out
- global shortcut Ctrl+Shift+Space on X11 (GNOME reserves Alt+Space); tray
'Quick Chat' item is the Wayland entry point; blur/Esc/close-request hide
- sends via the CLI agent turn with --message-file staged 0600 (argv is
world-readable procfs) and cleaned up after the turn; failures surface as
desktop notifications; Ctrl+Enter also opens the dashboard
- default-agent identity chip (emoji/monogram) from agents list --json, 60s cache
- window-scoped capability + permission set; window-state denylist; docs
* chore(linux): register quickchat.js as a knip native-asset entry
The companion now rings the doorbell for gateway approvals: it polls pending
node and device pairing requests over the existing CLI seam on the connected
watchdog cadence, fires one native notification per new request when the
window is unfocused, and shows a pending count in the tray status line.
Approval itself stays in the dashboard/CLI (owner boundary).
Adds a CmdOrCtrl+Shift+O summon shortcut with a tray toggle whose opt-out
persists as a marker file (no config schema). The X11-only global-hotkey
backend is gated off on native Wayland sessions — the feature is omitted
rather than half-registered through XWayland; a portal implementation can
follow.
Single-instance + openclaw:// deep links (openclaw://dashboard opens and
connects the dashboard; the tiny URL contract is routed entirely in Rust), a
Start at Login tray toggle (tauri-plugin-autostart, default off), native
notifications for update-ready/update-available when the window is unfocused,
and window-state persistence (canvas window excluded). All official Tauri v2
plugins; cross-platform deps so the macOS/Windows test builds keep compiling.
* feat(migrate): add Hermes memory-only import and a shared memory-import core
* feat(onboarding): offer detected memory imports during CLI setup and guided onboarding
* feat(ui): show a first-run memory-import offer in Control UI onboarding mode
* feat(linux-app): open the first-run dashboard in onboarding mode
* feat(macos): add the onboarding memory-import page
* docs: document the onboarding memory-import page across surfaces
* chore(i18n): translate onboarding memory-import strings for control-ui and native locales
* refactor: keep memory-import internals unexported for deadcode gates
* fix(ci): resolve lint findings in onboarding memory import
* chore(i18n): refresh native inventory after lint refactor
* fix(ci): skip updater artifacts in the unsigned Linux companion PR lane
* chore(i18n): reconcile locale artifacts after rebase
Sparkle-parity self-update for the apps/linux companion: tauri-plugin-updater +
process, checked silently on launch and from a "Check for Updates" tray item /
manual trigger. AppImage installs download, verify the signed update, and wait
for a user-confirmed restart; package-managed (deb) installs get a release-page
notice instead of replacing files the system package manager owns. The
linux-app release workflow signs the AppImage and publishes a signed latest.json
manifest that the app verifies with the committed minisign public key.
New discovery.rs browses _openclaw-gw._tcp with a lifetime mdns-sd
browser, keeps a fullname-keyed snapshot (resolved updates replace,
goodbye/TTL removes), and mirrors the native clients' TXT contract and
direct-selection gate (resolved SRV endpoint routing; tailnetDns is a
hint only). The bootstrap page lists discovered gateways and connecting
navigates the dashboard WebView to the resolved endpoint, validated
against the live snapshot; remote selection cancels the local watchdog
so an in-flight local bootstrap cannot steal the WebView back.
The desktop companion crate was Linux-only. All platform-specific code
lives in canvas.rs (WebKitGTK, cairo, unix sockets, SO_PEERCRED), so:
move webkit2gtk/cairo-rs/libc under linux target dependencies, gate the
canvas module, its protocol registration, invoke handler, and exit
shutdown to Linux, add a USERPROFILE home fallback and an explicit
no-op installer error for Windows, and add the multi-size icon.ico
Windows resource. Linux behavior is unchanged; macOS/Windows builds are
for testing (macOS cargo build+test green locally; Windows aarch64-msvc
built and launched in a Windows 11 ARM VM).
* feat(linux): canvas via CLI-node + Tauri app IPC bridge
* refactor: extract gateway helper modules
* build(linux-canvas): register plugin package in lockfile
* fix(linux-canvas): move canvas advertise test out of core, regen docs/protocol/deadcode
* fix(gateway): break node-catalog/registry import cycle via leaf normalize module; add canvas glossary term
* style: oxfmt invoke.ts and runtime.ts after buildNodeEventParams extraction
* fix(linux): load Canvas WebView via dedicated data_directory context
Wry's Linux/WebKitGTK incognito mode discards Tauri's registered
WebContext (wry webkitgtk/mod.rs), so the Canvas window got a fresh
ephemeral context without the openclaw-canvas:// scheme handler — the
bundled A2UI page never committed (stayed about:blank) and every A2UI
command timed out. Use an isolated cache-backed data_directory instead,
which keeps the protocol handler while still isolating Canvas storage
from the dashboard window.
* fix(linux): keep Canvas WebView ephemeral via incognito + data_directory
Autoreview flagged that a dedicated data_directory alone persists Canvas
browser state (cookies, localStorage, IndexedDB, service workers) across
restarts, so an agent that navigates Canvas to a site could leak an
authenticated session into a later session. iOS uses a non-persistent
store; Linux should match.
Add .incognito(true) alongside .data_directory(): the distinct directory
gives Tauri a fresh WebContext key so it still attaches the
openclaw-canvas:// protocol closure, and incognito makes Wry swap in a
fresh *ephemeral* context carrying those protocols. Live-verified on a
Wayland/WebKitGTK box: the bundled page still loads
(location.href=openclaw-canvas://localhost/index.html, openclawA2UI
present, A2UI renders) and the canvas-webview dir holds no persistent
cookie/storage files.