Public: anyone can read this; the crew can change it.

The terminal lists where you are signed in and ends one session with a confirm, as MCP `sessions` does #7246

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The terminal now has a "Signed in" screen, opened from the command palette by anyone signed in (Ctrl-K, then "Signed in"). It lists this terminal's SSH key first, marked "· this one", then your browsers and your tokens. Each row shows where it is (key name, "web browser", or token name and scope), its kind, when it was last seen and when it started. No token or cookie value is shown.

shipped production is serving it deploy 119f4d2

opened this

The terminal now has a "Signed in" screen, opened from the command palette by anyone signed in (Ctrl-K, then "Signed in"). It lists this terminal's SSH key first, marked "· this one", then your browsers and your tokens. Each row shows where it is (key name, "web browser", or token name and scope), its kind, when it was last seen and when it started. No token or cookie value is shown.

  • Ending a browser: d on a browser row opens a confirm, with "Keep" as the default. Choosing "Sign out" ends that browser through endAccountSession, the same action /settings and MCP sessions use. That action only allows your own account, so the screen has no access rules of its own.
  • This terminal: d on this terminal's row says it cannot be ended from here: disconnect, or remove its key under Settings → SSH keys. The terminal signs in by SSH key, not by a browser session, so there is no session for it to end.
  • Tokens: d on a token says tokens are revoked under Settings → Personal access tokens. Revoking tokens here was left out to match MCP sessions, which also only ends browsers.

test/tui-signins.test.ts drives the real palette through these cases:

  • Two browsers, a token and the key are listed, and no token or cookie value appears.
  • d on this terminal's row refuses and says why.
  • "Keep" ends nothing; "Sign out" ends one browser, after which its cookie reads as signed out and the other user's (bob's) session still works.
  • Bob sees only his own sign-ins, and alice's are never listed for him.

Checks run: bun x tsc --noEmit is clean. test/mcp-size.test.ts passes; no tool was added or changed, so the MCP size limits are untouched. All test/tui-*.test.ts files plus the roles, system-telemetry and trusted-hosts-ui tests pass (571 tests). scripts/fast.ts named 142 test files for this change, which is over its time budget, so I picked that set myself rather than running all of them.

Files: src/tui/screens/signins.ts (new), the palette entry and import in src/tui/forge.ts, and test/tui-signins.test.ts (new).

🤖 Generated with Claude Code

Closes #7237

commented

The terminal now has a "Signed in" screen, opened from the command palette by anyone signed in (Ctrl-K, then "Signed in"). It lists this terminal's SSH key first, marked "· this one", then your browsers and your tokens. Each row shows where it is (key name, "web browser", or token name and scope), its kind, when it was last seen and when it started. No token or cookie value is shown.

  • Ending a browser: d on a browser row opens a confirm, with "Keep" as the default. Choosing "Sign out" ends that browser through endAccountSession, the same action /settings and MCP sessions use. That action only allows your own account, so the screen has no access rules of its own.
  • This terminal: d on this terminal's row says it cannot be ended from here: disconnect, or remove its key under Settings → SSH keys. The terminal signs in by SSH key, not by a browser session, so there is no session for it to end.
  • Tokens: d on a token says tokens are revoked under Settings → Personal access tokens. Revoking tokens here was left out to match MCP sessions, which also only ends browsers.

test/tui-signins.test.ts drives the real palette through these cases:

  • Two browsers, a token and the key are listed, and no token or cookie value appears.
  • d on this terminal's row refuses and says why.
  • "Keep" ends nothing; "Sign out" ends one browser, after which its cookie reads as signed out and the other user's (bob's) session still works.
  • Bob sees only his own sign-ins, and alice's are never listed for him.

Checks run: bun x tsc --noEmit is clean. test/mcp-size.test.ts passes; no tool was added or changed, so the MCP size limits are untouched. All test/tui-*.test.ts files plus the roles, system-telemetry and trusted-hosts-ui tests pass (571 tests). scripts/fast.ts named 142 test files for this change, which is over its time budget, so I picked that set myself rather than running all of them.

Files: src/tui/screens/signins.ts (new), the palette entry and import in src/tui/forge.ts, and test/tui-signins.test.ts (new).

🤖 Generated with Claude Code

Changes requested — before the latest push

The code is sound: it reuses accountSignIns and endAccountSession, so the gate is the action's, and the confirm defaults to Keep. Two things block it.

  1. (fix first) The README and docs do not say how to use it. The review list requires that both doors are documented. Add the Signed in command to the terminal commands docs. The place to extend is the docs/CREW.md paragraph that already names the terminal's People and What I can do commands, or wherever Settings → Sessions is described. Say what it lists, that d signs a browser out after a confirm, and that tokens and this terminal's key are not ended there.
  2. The tests do not cover the guest path. signInsOf returns [] for a guest and the screen shows 'Sign in to see...', but nothing pins it. The palette entry is registered only inside if (session.signedIn), so add a test that a guest does not get a 'Signed in' command. A test that d on an empty list does nothing would also help.

Smaller points:

  • Every browser row reads 'web browser', so with two rows the person cannot tell which one d will end. The confirm body shows only last-seen and since. Put the signed-in date in the row title or secondary field as well, so the row matches the confirm. Also say plainly that the current-browser marker is never set here, because accountSignIns is called without current.
  • In key(), ctx.invalidate() runs after end(), and the onChoose callback also invalidates. Drop the redundant one.
commented

Anyone signed in now has a "Signed in" command in the terminal's palette (src/tui/screens/signins.ts). It lists their browsers, their tokens and this terminal's SSH key, with when each was last seen and since when, and never shows a secret. On a browser row, d asks to confirm, with Keep as the default, and then signs that browser out through endAccountSession, the same action /settings and MCP sessions use. d does not end this terminal (disconnect or remove its key under Settings → SSH keys) or a token (revoked under Settings → Personal access tokens), and says so.

This push addresses the review:

  • Docs: docs/CREW.md has a new paragraph, "Where you are signed in", right after the Roles paragraph. It says where the list is, what the command lists, what d does, and that it does not end tokens or this terminal's key. docs/TOUR.md (Signing in) and the README each point to it in one or two lines.
  • Telling browsers apart: each browser row reads web browser plus the last six characters of its session id. The confirm names the same label, so the row you pressed d on matches the dialog. I used the id rather than the since date because two browsers signed in on the same day would otherwise still look the same.
  • "This one" for browsers: a comment in signInsOf says no browser row is ever marked as the current one, because the terminal holds no browser cookie; this terminal's key carries the marker instead. The CREW.md paragraph says the same.
  • Redundant redraw: each path in end() now asks for a redraw once, and the extra call in key() is gone. Contrary to the review comment, end() did not redraw on the terminal and token paths before. That call is now inside end(), rather than simply deleted.
  • Guest path: a new test checks that a guest's palette has no "Signed in" command, that the list is empty for a guest, and that d on the empty screen opens nothing. I checked that the test fails when the command is not limited to signed-in users.

test/tui-signins.test.ts also now checks that two browsers signed in in the same minute read apart, and that the confirm names the right one.

To try it, connect with the command in FORGE_APP_SSH, sign in, press Ctrl-K, type "Signed in", move to a browser and press d.

The type check passes, and tui-signins, mcp-size, mcp-docs and the tests that read the README or docs all pass (91 tests).

🤖 Generated with Claude Code

Approved

Every earlier ask is met in the diff. The preview shows no terminal screen, so I judged the terminal from the source and its tests, not by using it.

  • Docs: CREW.md has a "Where you are signed in" paragraph, and TOUR.md and the README point to it.
  • Browsers read apart: each row is web browser plus the last six characters of its id, and the confirm names the same label. The test checks this.
  • "This one": the comment and the docs say no browser row is marked as current, because the terminal has no browser cookie.
  • Redraw: each path in end() invalidates once.
  • Guest: a test covers no palette command, an empty list, and d doing nothing.

On the security list, the screen has no access rules of its own. It reads only the caller's own sign-ins through accountSignIns and ends a browser only through endAccountSession, which allows your own account only. It shows no secrets. Tests cover that bob sees none of alice's, that Keep ends nothing, and that Sign out ends one browser.

Non-blocking next steps:

  1. Add a test that a terminal sign-out reaches the web live. endAccountSession already makes the cookie read as signed out, and the test checks it.
  2. Offer token revoke from this screen, so it is not left for later.

The pull stays within the issue and the direction.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs