Skip to content

Windows: sign in to Claude directly from the usage card - #257

Merged
vinzdg merged 1 commit into
vinzdg:mainfrom
rustamnova:codex/windows-claude-sign-in
Sep 20, 2026
Merged

vinzdg merged 1 commit into
vinzdg:mainfrom
rustamnova:codex/windows-claude-sign-in

Conversation

@rustamnova

Copy link
Copy Markdown
Contributor

Proposal

Thanks for adding Windows token renewal. This builds on current main instead of replacing or duplicating Renewer: when the user really needs to sign in again, the Claude hover card should offer an actionable Sign in button instead of requiring them to find a terminal themselves.

Changes

  • Add Sign in and provider-only Refresh controls to the Claude hover card, available in both healthy and failed states. Copy uses the existing translation path, with Russian translations and English fallback for other locales.
  • Launch the existing standalone Claude Code CLI's auth login --claudeai in a visible console on an explicit click. The official CLI opens the browser, handles PKCE/callback or pasted code, and writes its credential store. The widget handles no OAuth codes and exposes no tokens over IPC.
  • Wake the usage poller after login finishes, without restarting the widget. Show busy/cancelled/failed states, prevent duplicate clicks, and serialize interactive login with the existing background renewer.
  • Bound interactive login to 15 minutes; on timeout terminate/reap only the child tree created for that attempt. Use a fixed PowerShell script and pass the CLI path as environment data, not interpolated shell source. Reuse standalone-CLI discovery, including WinGet, while retaining Desktop-owned CLI exclusion.
  • Keep Refresh subject to the existing Claude Retry-After deadline. No changes to automatic-renewal timing/backoff or quota percentage semantics.
  • Treat empty credential tokens as signed out. Report HTTP 403 as access/network refusal rather than proof that sign-in expired; retain the 401 credential reread and 429 backoff paths.
  • Allow a tall card to scroll so the controls remain reachable. Add Rust regressions, a marker-extracted production-JS test, and wire the latter into Windows CI.

Scope

Windows only. No macOS changes, new dependency, hard-coded proxy, machine-specific paths/settings, account data, credential backups, Codex/Astra changes, or modifications to #210. The new login path does not send an inference prompt; the existing upstream renewal mechanism is left in place.

Validation

  • cargo build --locked
  • cargo test --locked: 103 passed; 2 existing opt-in live tests ignored.
  • cargo clippy --all-targets --locked: existing type_complexity warning in tray.rs; no new diagnostics. Strict -D warnings reports that existing warning, so this is not claimed as warning-free.
  • node scripts/check-ui-scripts.mjs
  • node scripts/test-claude-auth-ui.cjs: labels/localization, escaped messages, duplicate-click guard, refresh backoff feedback and launch errors.
  • git diff --check

A local prototype's card → native CLI → browser sign-in → fresh quota response was tested interactively on Windows without restarting the widget. This PR ports that path onto the newer main layout and its existing renewal implementation; the complete rebased application was not installed over the customized local build. No forced live token expiry or macOS tests were run. GitHub CI results are separate from these local checks.

If you prefer a narrower change, the 403/empty-token diagnostics can be split out; the primary proposal is discoverable, user-initiated Claude sign-in directly from the card.

@vinzdg

vinzdg commented Sep 19, 2026

Copy link
Copy Markdown
Owner

Thanks @rustamnova — signing in through claude auth login so the app never sees the token is exactly the right design, and the environment scrubbing is careful. Two fixes before merging:

  1. Lock placement: maybe_renew now takes the lock before the should_renew check (usage.rs ~287), so the busy flag flips on every poll and a Sign in click can be refused as "already running" at random. Please take the lock after the check.
  2. Timeout kill: the 15-minute taskkill /T /F kills the whole process tree, which can include a browser the CLI opened. Killing just the PowerShell/CLI process, or not force-killing the tree, would be safer.

No CI has run on it yet; a push will trigger it. Note that #269 (Linux) would move windows/ to desktop/; if that lands first this will need a rebase.

@vinzdg

vinzdg commented Sep 20, 2026

Copy link
Copy Markdown
Owner

Merged as 42b4bc1. This is the best-designed of the Windows PRs in this batch — running the CLI's own browser login in its own console so the OAuth never enters Codenotch is exactly right. No token or code crosses the IPC boundary (AuthState is {busy, message} with static strings), no URL is opened from a response, no embedded webview, the path goes through env rather than string interpolation, and CLAUDECODE/CLAUDE_CODE_*/CLAUDE_CONFIG_DIR/ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN are all stripped. The 403-vs-expired split matches the Mac's "codes only" rule too. Thanks @rustamnova.

The README conflict was just your section and #210's landing at the same spot — kept both.

One follow-up, d984f73: the four MESSAGE.lock().unwrap() calls panic if the mutex is ever poisoned, and since state() is read on every card render, one panic under the lock would take the sign-in UI down permanently. They now go through a small message() helper using unwrap_or_else(|e| e.into_inner()) — it guards one line of status text, which isn't worth crashing over.

Two nits left alone: maybe_renew shares the busy gate, so a silent background renewal shows the button disabled and labelled "Signing in…"; and the Sign in / Refresh buttons render on the Claude card unconditionally rather than only on needsAuth.

vinzdg added a commit that referenced this pull request Sep 20, 2026
The polling loop conflicted structurally with #257, which landed earlier today:
both rewrote it. Took this branch's per-account loop and re-applied #257's four
hooks onto it — the sign-in gate shared with renewal, the busy check that stops
polling a credential mid-login, clearing the card's message on a good reading,
and keeping 403 as access denied rather than folding it back into needsAuth.
Also kept the empty-token guard, and the sign-in row now keys on the account's
base so a second account gets it too.

Thanks @lucaedulima.
vinzdg added a commit that referenced this pull request Sep 20, 2026
#279's rewrite of usage.rs restored the private signature, but #257's
claude_auth calls it to locate the CLI.
abhishekthakur7 pushed a commit to abhishekthakur7/codenotch that referenced this pull request Sep 28, 2026
Runs the Claude Code CLI's own browser login in a terminal of its own, so the
OAuth never enters Codenotch: no token or code crosses the IPC boundary, no
embedded webview, and the inherited Claude environment is stripped. README
conflict was two unrelated sections appended at the same place; kept both.

Thanks @rustamnova.
abhishekthakur7 pushed a commit to abhishekthakur7/codenotch that referenced this pull request Sep 28, 2026
…laude

The polling loop conflicted structurally with vinzdg#257, which landed earlier today:
both rewrote it. Took this branch's per-account loop and re-applied vinzdg#257's four
hooks onto it — the sign-in gate shared with renewal, the busy check that stops
polling a credential mid-login, clearing the card's message on a good reading,
and keeping 403 as access denied rather than folding it back into needsAuth.
Also kept the empty-token guard, and the sign-in row now keys on the account's
base so a second account gets it too.

Thanks @lucaedulima.
abhishekthakur7 pushed a commit to abhishekthakur7/codenotch that referenced this pull request Sep 28, 2026
vinzdg#279's rewrite of usage.rs restored the private signature, but vinzdg#257's
claude_auth calls it to locate the CLI.
abhishekthakur7 pushed a commit to abhishekthakur7/codenotch that referenced this pull request Sep 28, 2026
Runs the Claude Code CLI's own browser login in a terminal of its own, so the
OAuth never enters Codenotch: no token or code crosses the IPC boundary, no
embedded webview, and the inherited Claude environment is stripped. README
conflict was two unrelated sections appended at the same place; kept both.

Thanks @rustamnova.
abhishekthakur7 pushed a commit to abhishekthakur7/codenotch that referenced this pull request Sep 28, 2026
…laude

The polling loop conflicted structurally with vinzdg#257, which landed earlier today:
both rewrote it. Took this branch's per-account loop and re-applied vinzdg#257's four
hooks onto it — the sign-in gate shared with renewal, the busy check that stops
polling a credential mid-login, clearing the card's message on a good reading,
and keeping 403 as access denied rather than folding it back into needsAuth.
Also kept the empty-token guard, and the sign-in row now keys on the account's
base so a second account gets it too.

Thanks @lucaedulima.
abhishekthakur7 pushed a commit to abhishekthakur7/codenotch that referenced this pull request Sep 28, 2026
vinzdg#279's rewrite of usage.rs restored the private signature, but vinzdg#257's
claude_auth calls it to locate the CLI.
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.

2 participants