Skip to content

feat(web): in-app profile switcher dropdown for multi-daemon navigation (#814) - #815

Merged
eaitbrahim merged 2 commits into
mainfrom
feat/profile-switcher-814
Sep 24, 2026
Merged

eaitbrahim merged 2 commits into
mainfrom
feat/profile-switcher-814

Conversation

@eaitbrahim

Copy link
Copy Markdown
Contributor

Summary

Closes #814.

Implements an in-app profile switcher dropdown in Keel's Honest Cockpit (keel serve / keel open), enabling operators to switch directly between running console daemons (live, paper, hourly, equities) via the browser header without invoking terminal commands.

Changes

  • Runtime Peer Discovery (keel/web/runtime.py): Added profile and mode to serving runtime record; added live_peers(current_port) to discover running daemon files in ~/.keel/run/, verify PID liveness and port connectivity, and clean up stale files.
  • Server Integration (keel/web/server.py): Supplies active profile name and auto-trade mode when recording serving status.
  • Config Payload (keel/web/payload.py, keel/web/api.py): Serializes verified peer metadata with wire-safe types.
  • Frontend UI & Event Wiring (keel/web/static/js/render.js, main.js, keel.css):
    • sessionChip: Renders <select class="profile-select"> when multiple active daemons exist, or static text when single.
    • deploymentCard: Lists all active profile peers.
    • main.js: Delegated change event navigates browser to chosen daemon URL.
    • keel.css: Light/dark theme styling for profile selector dropdown.
  • Tests (tests/web/test_profile_switcher.py): 5 unit tests covering record writing, peer discovery, stale cleanup, wire types, and DOM structure.

Verification

  • uv run pytest tests/web/: 977 passed in 153.91s
  • uv run ruff check keel/web/ tests/web/: All checks passed!
  • uv run mypy keel/web/: Success: no issues found in 9 source files

- Discover active peer console daemons in runtime directory (~/.keel/run/)
- Verify PID liveness and port reachability before serving peer metadata
- Expose peers array in /api/config payload
- Render <select class="profile-select"> in header session chip when multiple daemons are active
- Add change listener to navigate between daemon URLs
- Display active profile peers in deployment card
- Add comprehensive unit tests in tests/web/test_profile_switcher.py
@eaitbrahim
eaitbrahim merged commit 9f7524b into main Sep 24, 2026
4 checks passed
@eaitbrahim
eaitbrahim deleted the feat/profile-switcher-814 branch September 24, 2026 22:52
eaitbrahim added a commit that referenced this pull request Sep 25, 2026
…hip (#815 review) (#816)

#815 put every running console's `http://.../?token=` into /api/config
and into <option value>: the paper console's page held the live
console's session token, and its own, which security.py says must never
be written into the page. It also rendered an empty select
(`el()` has no children form), named a different console as this one
when this console had no record, showed a start-up mode that goes
stale, and made the session chip a dropdown, which #704 forbids.

- The page gets {label, href} per OTHER console; href is a token-free
  /switch/<port> on this console. server._switch reads the peer's
  record when followed and 303s into the peer's own ?token= hand-off,
  so only the browser's navigation carries the token (as `keel open`
  does). Local only: security.local_deployment, shared with
  gated_action_permitted, plus a loopback peer.
- sw.js declines /switch/ navigations to the network; answered from the
  cached shell they would silently do nothing.
- The switcher is its own control, #console-switcher, beside the chip.
  sessionChip is back to its pre-#815 code and still passes #704's
  no-interactive-node rule; the new control has a matching rule.
- This console is never read from a record; no mode in labels; the
  record no longer stores one.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
eaitbrahim added a commit that referenced this pull request Sep 25, 2026
…e switcher with no tokens (#818)

MINOR: #810 and #814/#816 add console features. No schema change since
0.16.0, so a deployment needs no `keel migrate` step beyond the usual
idempotent run.

What lands:

  #807 -- a quote-increment cache record predating quote_increment is a
  MISS, not an "unknown".
  #810 -- the refused view says how to get the address it asks you to
  paste; #817 fixes its duplicate `const note`, which blanked the console.
  #813 (#812) -- the cycle line ends `vetoed=N (rail, ...)`, so a rail
  veto no longer reads as a silent drop.
  #815 + #816 (#814) -- a console switcher beside the session chip.
  /api/config carries no session token; /switch/<port> redirects into the
  chosen console's own hand-off, local deployments only.

Eight workspace versions and the inter-package `==` pins move together,
plus the refreshed uv.lock the release workflow checks first.

Co-authored-by: Claude Opus 5.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.

feat(web): in-app profile switcher dropdown for multi-daemon navigation

1 participant