Skip to content

feat(desktop): cloud mode — connect the desktop to a remote control plane - #845

Open
AbirAbbas wants to merge 4 commits into
mainfrom
feat/desktop-cloud-mode
Open

feat(desktop): cloud mode — connect the desktop to a remote control plane#845
AbirAbbas wants to merge 4 commits into
mainfrom
feat/desktop-cloud-mode

Conversation

@AbirAbbas

@AbirAbbas AbirAbbas commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

The final piece of the one-management-path arc (#837#841#842#843#844): AgentField Desktop can now connect to a remote control plane with a saved {server_url, api_key} profile, managing agents on that box exactly the way it manages a local one — same client, same flows, only the base URL changes.

What's new

Cloud tab (agentfield://cloud, ⌘/Ctrl+5): connection status, server URL + API key form, live Test connection verdict (reachable / auth / install API / server version), Save & switch, and Switch back to local (keeps the saved profile for one-click re-enable). A Deploy on Railway section opens the control-plane template with paste-back instructions, via a dedicated IPC channel — no generic URL-opener hole.

Connection core: a new cycle-free connection-state module supplies base URL + API key to cpClient and the five raw dashboard/tray fetches that previously bypassed auth entirely. URL normalization refuses plaintext http:// to public hosts, so keys can't leak over cleartext. The cloud profile is applied at startup and immediately on settings change, and survives restarts (normalizeSettings extended — it silently drops unknown keys otherwise).

Guardrails in cloud mode: no local port probing, no af server spawn, no agent autostart (one remote health check instead), local-server-start IPC refuses politely, skills sync skipped. Switching back restores previous local behavior exactly.

Also fixes a latent bug: fetchUsageStats ignored the active base URL.

Validation

  • Live E2E (5/5) against a real CP running with AGENTFIELD_API_KEY enforcement: full management lifecycle (install → secrets → start → stop → uninstall) driven through the cloud profile; test-connection matrix verified live (valid key / rejected key / unreachable); autostart gating and local-mode restore confirmed. Notable: with a key configured, the CP properly 401s missing/wrong keys — fail-closed as required.
  • 339 desktop tests green (up from 323), typecheck clean, dist:dir builds.
  • API key is stored in the desktop settings JSON for now (same trust level as existing ~/.agentfield config material); moving it to Electron safeStorage is a noted follow-up.

Follow-ups (not in this PR)

  • Railway template v2 (point at the control-plane-cloud image + volume + generated key default)
  • Automated Railway deploy via loopback+PKCE OAuth (needs an OAuth client registration)
  • Electron safeStorage for the API key at rest

🤖 Generated with Claude Code

AbirAbbas and others added 2 commits July 30, 2026 19:45
New connection-state module (base URL + API key, cycle-free) that
cpClient and every raw dashboard/tray fetch now draw auth from; a cloud
settings profile ({enabled, serverUrl, apiKey}) persisted through
normalizeSettings; testCloudConnection (health, auth, install-API,
version probes with per-step timeouts); URL normalization that refuses
plaintext http to public hosts; autostart gating so cloud mode never
probes ports, spawns a server, or auto-starts agents — one remote
health check instead. Fixes fetchUsageStats ignoring the active base
URL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rol plane

New Cloud nav view (agentfield://cloud, Cmd/Ctrl+5): connection status,
server URL + API key form with live test verdict (reachable / auth /
install API / version), save-and-switch, and switch-back-to-local that
keeps the saved profile. Deploy-on-Railway section opens the control
plane template through a dedicated IPC channel (no generic URL opener).
Profile is applied at startup and immediately on settings change; the
local server start IPC refuses politely while a cloud profile is
active, and skills sync is skipped in cloud mode.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AbirAbbas
AbirAbbas requested a review from a team as a code owner July 30, 2026 23:46
AbirAbbas and others added 2 commits July 30, 2026 20:49
… plane

A freshly deployed control plane returns {"packages":null,"total":0}
(Go marshals nil slices as null), which crashed the Agents view with
'Cannot read properties of null (reading filter)' the moment a cloud
profile pointed at an empty CP. Normalize every list-shaped response at
the cpClient boundary (packages, install jobs, job lines, running
agents, agent/global secrets) and guard the raw node/execution readers.
Regression tests use the exact wire payloads; verified live against an
empty key-enforcing CP.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Five explicit states with color: in-flight test (spinner, locked
controls), green success with server version, amber degraded verdict
for outdated control planes (connected but too old for desktop agent
management), red failure with per-check pass/fail marks, and a green
confirmation after save/switch with a live status dot. Alignment pass:
stacked full-width fields, overlaid show/hide toggle, single action
row, fixed-column verdict list, flush Railway steps. Reduced-motion
fallback for the spinner.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant