auth: show user profile in `auth status` via core GET /me · Entire

auth: show user profile in auth status via core GET /me

3b28626→main·

toothbrush·1mo ago·2 files·+195 added/-49 removed

entire auth status now calls the core API's GET /me, which both validates the stored token (liveness) and supplies a profile header:

Logged in to https://us.auth.entire.io User: Alice Smith (@alice) alice@example.com Identity: github/alice Token: stored in OS keychain

Active sessions: ...

/me is the primary liveness gate (a 401 surfaces as *coreapi.ErrorModelStatusCode, now recognised by isKeychainTokenRejected). The active-sessions list runs after, on the data API; since the token is already known good, a list failure degrades to a stderr warning instead of failing the command. Empty profile fields are omitted.

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Sessions

5ca10643dc53View transcript

Refactor Auth Commands for Session Management

Changes

2

12 unmodified lines

// authProfile is the subset of the core API's GET /me that entire auth // status renders. type authProfile struct { Handle string DisplayName string Email string Provider string ProviderUserID string }

// profileFetcher fetches the logged-in user's profile via GET /me on the core // API. Injected so status stays unit-testable without a live core. type profileFetcher func(ctx context.Context) (*authProfile, error)

// defaultFetchProfile fetches the current user's profile from the core API's // GET /me. It doubles as the liveness check for entire auth status: a 401 // (or an expired login that can't be exchanged) means the stored token is no // longer usable, which isKeychainTokenRejected maps to a re-login hint. func defaultFetchProfile(ctx context.Context) (*authProfile, error) { client, err := coreapi.New() if err != nil { return nil, fmt.Errorf("connect to Entire control plane: %w", err) } me, err := client.GetMe(ctx) if err != nil { return nil, fmt.Errorf("fetch profile: %w", err) } p := &authProfile{ Provider: me.Auth.Provider, ProviderUserID: me.Auth.ProviderUserId, } p.Handle, _ = me.Global.Handle.Get() if reg, ok := me.Regional.Get(); ok { p.DisplayName, _ = reg.DisplayName.Get() p.Email, _ = reg.Email.Get() } return p, nil }

// writeProfileLines renders the user identity from GET /me as aligned // label/value lines, omitting any field the server didn't populate. func writeProfileLines(w io.Writer, p *authProfile) { var parts []string if p.DisplayName != "" { parts = append(parts, p.DisplayName) } if p.Handle != "" { parts = append(parts, "@"+p.Handle) } if p.Email != "" { parts = append(parts, "<"+p.Email+">") } if len(parts) > 0 { fmt.Fprintf(w, " %-9s %s\n", "User:", strings.Join(parts, " ")) } if p.Provider != "" { identity := p.Provider if p.ProviderUserID != "" { identity += "/" + p.ProviderUserID } fmt.Fprintf(w, " %-9s %s\n", "Identity:", identity) } }

// sortSessionsByRecency orders sessions most-recently-used first, then most // recently created, then by id — a fully specified order independent of the // server's response ordering.