Skip to content

feat: mirrorstack logout#16

Merged
I-am-nothing merged 2 commits into
mainfrom
feat/logout-command
May 4, 2026
Merged

feat: mirrorstack logout#16
I-am-nothing merged 2 commits into
mainfrom
feat/logout-command

Conversation

@I-am-nothing

Copy link
Copy Markdown
Contributor

Summary

New `mirrorstack logout` subcommand: revokes the current session on the platform and wipes the local credentials file.

```
$ mirrorstack logout
✓ signed out.
```

Behaviour

  • Already signed out (no creds file): prints "✓ already signed out", exits 0.
  • Happy path: DELETE /v1/auth/sessions/current with refresh token in body (CLI flow), then `fs::remove_file` on the creds.
  • Server 401: treat as already-revoked (platform handler is idempotent), wipe local creds, success.
  • Server failure (network / 5xx): still wipe local creds and exit 0, with a yellow warning naming the error and noting the refresh token may remain valid until expiry. The user's intent is "log me out"; refusing to clear local state because the platform is unreachable would leave them stuck.

Implementation notes

  • `api::revoke_session` matches the `&Client` signature shape from the recent api.rs cleanup and routes through the shared `unexpected_body_error` helper.
  • `credentials::delete` is idempotent (NotFound → Ok) so the command body doesn't special-case missing files.
  • Output styling matches `module init` (`✓` green, warnings yellow, hints dim).

Test plan

  • `cargo fmt --check`
  • `cargo clippy --all-targets -- -D warnings`
  • `cargo test` — 27/27 (was 25; +2 mockito tests for revoke_session, +2 lifecycle steps for delete)
  • `cargo build --release`
  • `./target/release/mirrorstack logout --help` renders

I-am-nothing and others added 2 commits May 4, 2026 14:23
Revokes the current session via DELETE /v1/auth/sessions/current
(refresh token in body, CLI flow) and wipes the local credentials
file at ~/.config/mirrorstack/cli/credentials.json.

Behaviour:
- Already signed out (creds file missing) prints "✓ already signed
  out" and exits 0. No server call.
- Server revoke succeeds → "✓ signed out".
- Server returns 401 → treat as already-revoked (the platform's
  Revoke handler is idempotent), wipe local creds, success.
- Server fails (network, 5xx) → still wipe local creds, print a
  yellow warning naming the error and noting the refresh token may
  remain valid until expiry. Exit 0 — the user's intent is "log
  me out" and refusing to clear local state because the platform
  is unreachable would leave them stuck.

Implementation reuses the existing seams:
- \`api::revoke_session\` follows the &Client signature shape from
  the recent api.rs cleanup, runs through \`unexpected_body_error\`
  for non-401 failures.
- \`credentials::delete\` is idempotent (NotFound → Ok) so the
  command body doesn't need to special-case missing files.

## Tests
- 27/27 (was 25). New:
  - api::tests::revoke_session_204_is_ok — happy path with body match
  - api::tests::revoke_session_401_is_unauthenticated — 401 mapping
  - credentials_lifecycle now exercises delete() success + idempotent
    second call. Single test by design — env-var mutation in
    with_temp_config_dir would race if split.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Extract \`commands::ok_mark()\` and \`commands::warn_prefix()\` for the
  green-✓ and yellow-warning prefixes — same \`style(...).bold()\` chain
  was hand-rolled at 5 call sites across logout.rs and module.rs.
- \`credentials::delete\` error includes the file path so the user can
  manually remove it on permission errors instead of seeing a bare
  "permission denied".
- Drop the stale \`#[allow(dead_code)]\` on \`LoadError\` — \`NotFound\` is
  now matched in load(), load_or_login_hint(), and logout::run().
  Confirmed unnecessary by clippy passing without it.
- New test \`revoke_session_5xx_is_unexpected\` covers the unexpected
  body path that logout's \`server_warn\` branch exercises in practice.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@I-am-nothing I-am-nothing merged commit 722b818 into main May 4, 2026
5 checks passed
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