Skip to content

feat: one-click Claude CLI login from Settings - #121

Merged
nishant merged 1 commit into
masterfrom
feat/claude-login-button
Jul 14, 2026
Merged

nishant merged 1 commit into
masterfrom
feat/claude-login-button

Conversation

@nishant

@nishant nishant commented Jul 14, 2026

Copy link
Copy Markdown
Owner

What

A "Log in to Claude CLI" button in Settings → App → Claude (and in the Claude widget's "not available" panel) that opens a terminal running the CLI login flow and cleans up after itself — no more opening a terminal by hand when the widget needs a login.

The flow

  1. Click the button → a real terminal window opens running claude auth login --claudeai.
    • Investigated first: the installed CLI (2.1.201) has a dedicated claude auth login command — it opens the browser, waits for the OAuth callback, prints success, and exits. No interactive /login session, no keystroke injection needed. --claudeai pins subscription (Max plan) billing, never Console/API keys.
  2. What stays manual: just the browser OAuth approval — the terminal needs no typing at all.
  3. The main process (apps/main/src/claudeLogin.ts) watches for fresh CLI credentials every 2 s (5 min timeout):
    • Windows/Linux: ~/.claude/.credentials.json mtime bump (verified — that's where the CLI writes tokens on this machine).
    • macOS-only: the CLI stores creds in the keychain, so it polls security find-generic-password -s "Claude Code-credentials" without -w and diffs the mdat (modified date) attribute — metadata only, the secret never enters the process. Same service name packages/server's claudeUsage.ts already uses.
  4. On success: the terminal is auto-closed after a 1.5 s grace, and claude:login-finished is broadcast → useClaudeStatus invalidates claude-status + claude-usage so the widget flips available immediately (listener lives in the hook, so popouts work too).

Auto-close mechanics (and the wt decision)

  • Windows (primary): a hidden PowerShell helper runs Start-Process cmd -ArgumentList '/k',… -PassThru — ShellExecute guarantees a new visible console in both dev and packaged contexts (a direct spawn('cmd') would attach to the parent console under pnpm dev), and -PassThru returns the cmd PID: the killable root of the window's tree. On login, taskkill /PID <pid> /T /F closes the window. Verified live on this machine (spawn → PID captured → window visible → tree-killed → window gone).
  • Deliberately not wt.exe: the wt alias hands off to WindowsTerminal.exe and exits — its PID can't close the window, and killing WindowsTerminal.exe would nuke every open terminal tab. On Win11 the default-terminal setting routes the spawned cmd window into Windows Terminal anyway, so you still get a WT-looking window.
  • macOS (secondary): osascript → Terminal.app do script + activate; the returned "tab 1 of window id N" is parsed and that exact window is closed by id on success (best-effort). Caveat: if the keychain mdat doesn't change on a re-login, the watcher times out after 5 min and the window just stays open showing the CLI's success output.
  • Edge cases: second click while a flow is live → 'already-open' toast, no second terminal; user closes the window themselves → Windows liveness probe stops the watcher; timeout → watcher stops quietly, terminal left open. Already logged in → re-running the login is harmless; fresh creds still trigger auto-close.

Plumbing

  • IPC typed end-to-end: claude:open-login (invoke) + claude:login-finished (push) in IpcChannels/ElectronAPI, ClaudeLoginOpenResult exported from @dash/shared; preload wrappers follow the popout/clipboard patterns; handler in registerIpcHandlers.
  • Renderer: openClaudeCliLogin() helper in useClaude.ts (toasts results); buttons in SettingsModal and ClaudeWidget.
  • Unit tests (14, colocated claudeLogin.test.ts): platform spawn-spec shapes, PID / Terminal-window-id / keychain-mdat parsers, credentials-change detection.
  • Docs: CHANGELOG entry (PR feat: one-click Claude CLI login from Settings #121), CLAUDE.md Claude widget row updated.

Gates

  • pnpm test ✅ (all 4 packages; main now 33 tests incl. 14 new)
  • pnpm typecheck ✅
  • pnpm lint ✅
  • Runtime spot-check ✅ Windows spawn+kill mechanism verified live with a harmless echo command. The full end-to-end (button → terminal → browser login → auto-close) is left for Nish to try in the running app, since another session owns it — the mechanism underneath is the exact command verified above.

🤖 Generated with Claude Code

A "Log in to Claude CLI" button (Settings -> App -> Claude + the Claude
widget's unavailable panel) opens a real terminal window running
`claude auth login --claudeai` (dedicated login command verified against
CLI 2.1.201 - opens the browser, waits for the OAuth callback, exits).
The main process (apps/main/src/claudeLogin.ts) watches for fresh CLI
credentials - ~/.claude/.credentials.json mtime on Windows/Linux, keychain
"mdat" metadata on macOS (secret never read) - then auto-closes the
terminal (Windows: taskkill /T on the Start-Process-spawned cmd PID;
macOS: best-effort Terminal.app window close by id) and pushes
claude:login-finished so the widget refetches /api/claude/status
immediately.

Windows deliberately avoids wt.exe: the alias hands off to
WindowsTerminal.exe and exits, leaving no killable PID (and killing
WindowsTerminal.exe would close every open tab). The PowerShell
Start-Process cmd /k path guarantees a new visible window in both dev
and packaged contexts and was verified live (spawn + tree-kill).

New IPC: claude:open-login (invoke) + claude:login-finished (push),
typed end-to-end in packages/shared. Unit tests cover the spawn-spec
builder, PID/window-id/keychain parsers, and change detection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@nishant
nishant force-pushed the feat/claude-login-button branch from e82cfd5 to 0066ecd Compare July 14, 2026 06:02
@nishant
nishant merged commit e45c19e into master Jul 14, 2026
1 check 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