feat(cli): always-on paste fallback for auth login (Ink UI)#4072
Conversation
Drop the SSH/CI env-var sniff that gated the paste-code flow. `superset auth login` now races the loopback callback and the paste prompt in parallel — both UI affordances are always live, so remote (tmux, VS Code Remote, Codespaces, mosh) sessions get a working fallback even when SSH_CONNECTION isn't set. The auth screen renders via Ink so the "URL copied" toast is a real 1.5s React-state-driven notification, not append-only log spam. `c` copies the URL via OSC 52 (cross-device, works over SSH) with native pbcopy/clip/wl-copy/xclip as a local fallback. Esc / Ctrl-C now exit cleanly with "Login interrupted" instead of throwing.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (4)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds an Ink-based interactive login UI and clipboard helper, moves Ink/React to runtime dependencies, and refactors the CLI auth flow to concurrently race a loopback callback against an abortable pasted-code prompt with coordinated UI wiring and cleanup. ChangesInteractive Login + Auth Flow Refactor
sequenceDiagram
actor User
participant CLI as CLI / LoginUI
participant AuthLib as Auth Library
participant Server as Loopback Server
participant Browser as Browser / OAuth
participant Clipboard as Clipboard / OSC52
User->>CLI: start login (TTY)
CLI->>AuthLib: call login() with callbacks
AuthLib->>Server: bind loopback server
AuthLib->>CLI: onAuthorizationUrl(pasteURL)
CLI->>User: show URL + input UI
par Loopback Path
AuthLib->>Browser: open loopback authorization URL
User->>Browser: authenticate
Browser->>Server: redirect/callback with code
Server-->>AuthLib: deliver code + redirectUri
and Paste Path
User->>Clipboard: copy authorization code
User->>CLI: paste code (contains `#state`)
CLI->>AuthLib: resolve promptForPastedCode(code)
AuthLib->>AuthLib: parse & validate state
and Copy Helper
User->>CLI: press 'c' when input empty
CLI->>Clipboard: call copyToClipboard()
Clipboard-->>CLI: returns true/false
CLI->>User: show "Copied!" transient
end
AuthLib->>AuthLib: race completes with winning result
AuthLib->>AuthLib: exchange code for tokens
AuthLib-->>CLI: auth complete
CLI->>Server: close loopback server
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile Summary
Confidence Score: 4/5Safe to merge with minor UX and efficiency fixes recommended in command.ts Only P2 findings: a redundant Clack intro banner on the Ink code path, a duplicate network call, and a fragile string-equality cancel check. Core auth logic, race management, and clipboard implementation are solid. packages/cli/src/commands/auth/login/command.ts — success-path banner duplication and duplicate API call
|
| Filename | Overview |
|---|---|
| packages/cli/src/commands/auth/login/command.ts | Orchestrates the Ink/Clack dual-path login flow; contains three issues: a redundant p.intro call on the Ink success path, a duplicate api.user.me.query() network call, and fragile error detection via message string comparison. |
| packages/cli/src/commands/auth/login/LoginUI.tsx | New Ink React component for the auth login UI; correctly handles input, paste, clipboard copy keybinding (with empty-buffer guard), validation, and cancel via Esc/Ctrl-C. |
| packages/cli/src/commands/auth/login/copyToClipboard.ts | Clean implementation: OSC 52 first (works cross-SSH), then falls back to native clipboard tools; error handling is solid with the child process close/error listeners. |
| packages/cli/src/lib/auth.ts | Refactored to race loopback callback vs paste prompt using dual AbortControllers; loopback errors are silently swallowed to keep the paste path alive, which is intentional and correctly documented. |
| packages/cli/package.json | Adds ink ^7.0.1, react ^19.2.5, and @types/react ^19.2.14 as expected for the new Ink UI component. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[superset auth login] --> B{stdout & stdin TTY?}
B -- No --> C[Clack plain flow]
B -- Yes --> D[Render Ink LoginUI]
D --> E[bindLoopbackServer]
E --> F[buildAuthorizeUrl paste redirect URI]
F --> G[onAuthorizationUrl callback - update Ink UI with URL]
G --> H{shouldOpenBrowser?}
H -- Yes --> I[openBrowser loopback URL]
H -- No --> J[Race: loopback vs paste]
I --> J
J --> K[waitForCallback loopback AbortController]
J --> L[promptForPastedCode paste AbortController]
K -- callback wins --> M[pasteController.abort - onAbort resolves pastePromise]
L -- paste wins --> N[parsePastedCode - state check - callbackController.abort]
M --> O[exchangeCodeForToken loopback redirectUri]
N --> P[exchangeCodeForToken paste redirectUri]
O --> Q[update status done - unmount Ink]
P --> Q
Q --> R[writeConfig tokens]
R --> S[api.user.me.query - show name/email]
S --> T{org selection}
T --> U[writeConfig orgId - p.outro]
K -- error/timeout --> V[swallowed - paste path continues]
L -- user presses Esc --> W[pasteReject CLIError Login cancelled]
W --> X[caught - cancelled=true - p.cancel Login interrupted]
Comments Outside Diff (1)
-
packages/cli/src/commands/auth/login/command.ts, line 193 (link)Duplicate
api.user.me.query()callapi.user.me.query()is already called at line 129 to display the user's name and email. This line issues a second identical request just to read.idfrom the result. Theuservariable from line 129 is unreachable here because it's declared inside atryblock above. Lift it out of that block so the id can be reused without a second network round-trip.Prompt To Fix With AI
This is a comment left during a code review. Path: packages/cli/src/commands/auth/login/command.ts Line: 193 Comment: **Duplicate `api.user.me.query()` call** `api.user.me.query()` is already called at line 129 to display the user's name and email. This line issues a second identical request just to read `.id` from the result. The `user` variable from line 129 is unreachable here because it's declared inside a `try` block above. Lift it out of that block so the id can be reused without a second network round-trip. How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 3
packages/cli/src/commands/auth/login/command.ts:119-124
**Redundant `p.intro` banner printed twice on the Ink path**
When `inkInstance` is truthy, the Ink component already rendered `superset auth login` as its header. After `inkInstance.unmount()` in the `finally` block, this code calls `p.intro("superset auth login")` again, causing the banner to appear a second time in the terminal output before the `✔ Authorized!` line.
```suggestion
if (!inkInstance) {
p.log.success("Authorized!");
} else {
p.log.success("Authorized!");
}
```
### Issue 2 of 3
packages/cli/src/commands/auth/login/command.ts:193
**Duplicate `api.user.me.query()` call**
`api.user.me.query()` is already called at line 129 to display the user's name and email. This line issues a second identical request just to read `.id` from the result. The `user` variable from line 129 is unreachable here because it's declared inside a `try` block above. Lift it out of that block so the id can be reused without a second network round-trip.
### Issue 3 of 3
packages/cli/src/commands/auth/login/command.ts:94-99
**Fragile error identification by message string**
The check `err.message === "Login cancelled"` is a stringly-typed sentinel. If the message is ever changed (e.g. for i18n or copy consistency), the catch block will silently re-throw as an unhandled error instead of producing the clean `Login interrupted` exit. Consider tagging `CLIError` with a machine-readable code or adding a custom subclass (e.g. `LoginCancelledError`) so the check is refactoring-safe.
Reviews (1): Last reviewed commit: "feat(cli): always-on paste fallback for ..." | Re-trigger Greptile
| if (!inkInstance) { | ||
| p.log.success("Authorized!"); | ||
| } else { | ||
| p.intro("superset auth login"); | ||
| p.log.success("Authorized!"); | ||
| } |
There was a problem hiding this comment.
Redundant
p.intro banner printed twice on the Ink path
When inkInstance is truthy, the Ink component already rendered superset auth login as its header. After inkInstance.unmount() in the finally block, this code calls p.intro("superset auth login") again, causing the banner to appear a second time in the terminal output before the ✔ Authorized! line.
| if (!inkInstance) { | |
| p.log.success("Authorized!"); | |
| } else { | |
| p.intro("superset auth login"); | |
| p.log.success("Authorized!"); | |
| } | |
| if (!inkInstance) { | |
| p.log.success("Authorized!"); | |
| } else { | |
| p.log.success("Authorized!"); | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/cli/src/commands/auth/login/command.ts
Line: 119-124
Comment:
**Redundant `p.intro` banner printed twice on the Ink path**
When `inkInstance` is truthy, the Ink component already rendered `superset auth login` as its header. After `inkInstance.unmount()` in the `finally` block, this code calls `p.intro("superset auth login")` again, causing the banner to appear a second time in the terminal output before the `✔ Authorized!` line.
```suggestion
if (!inkInstance) {
p.log.success("Authorized!");
} else {
p.log.success("Authorized!");
}
```
How can I resolve this? If you propose a fix, please make it concise.| } catch (err) { | ||
| if (err instanceof CLIError && err.message === "Login cancelled") { | ||
| cancelled = true; | ||
| } else { | ||
| throw err; | ||
| } |
There was a problem hiding this comment.
Fragile error identification by message string
The check err.message === "Login cancelled" is a stringly-typed sentinel. If the message is ever changed (e.g. for i18n or copy consistency), the catch block will silently re-throw as an unhandled error instead of producing the clean Login interrupted exit. Consider tagging CLIError with a machine-readable code or adding a custom subclass (e.g. LoginCancelledError) so the check is refactoring-safe.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/cli/src/commands/auth/login/command.ts
Line: 94-99
Comment:
**Fragile error identification by message string**
The check `err.message === "Login cancelled"` is a stringly-typed sentinel. If the message is ever changed (e.g. for i18n or copy consistency), the catch block will silently re-throw as an unhandled error instead of producing the clean `Login interrupted` exit. Consider tagging `CLIError` with a machine-readable code or adding a custom subclass (e.g. `LoginCancelledError`) so the check is refactoring-safe.
How can I resolve this? If you propose a fix, please make it concise.
🧹 Preview Cleanup CompleteThe following preview resources have been cleaned up:
Thank you for your contribution! 🎉 |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli/src/commands/auth/login/command.ts`:
- Line 22: The current useInk variable (const useInk = process.stdout.isTTY &&
process.stdin.isTTY) will enable the Ink interactive UI in CI jobs that allocate
a pseudo-TTY; update its logic to explicitly opt-out when running in CI by
checking common CI environment flags (e.g., process.env.CI,
process.env.GITHUB_ACTIONS, process.env.GITLAB_CI, process.env.CIRCLECI) and
only enable Ink when both TTY checks pass and none of those CI indicators are
present (e.g., const useInk = process.stdout.isTTY && process.stdin.isTTY &&
!isCI). Locate the useInk declaration in command.ts and add a small helper or
inline check (isCI) to ensure CI environments take the plain fallback path.
- Around line 68-90: In promptForPastedCode, pass the abort signal into the
interactive prompt so p.text can be cancelled: update the p.text call inside
promptForPastedCode to include the signal option (so the prompt listens to the
provided AbortSignal), keeping the existing message and validate behavior and
still handling p.isCancel and signal.aborted checks; this affects the p.text
invocation used when inkInstance is falsy and ensures the prompt is torn down if
the passed-in signal fires.
In `@packages/cli/src/commands/auth/login/LoginUI.tsx`:
- Around line 103-105: The footer help text in LoginUI.tsx currently only shows
"Esc to cancel" but the input handler supports both Esc and Ctrl+C; update the
Text (dimColor italic) element in the LoginUI component so it mentions both keys
(e.g., "Esc or Ctrl+C to cancel" or "Esc / Ctrl+C to cancel") to accurately
reflect available shortcuts. Locate the Text node rendering the footer inside
LoginUI and replace the string while keeping styling and formatting intact.
In `@packages/cli/src/lib/auth.ts`:
- Around line 290-300: The pasteRedirectUri currently concatenates getWebUrl()
and PASTE_REDIRECT_PATH which can produce a double-slash when SUPERSET_WEB_URL
ends with '/' (breaking exact redirect matching). Update the code that builds
pasteRedirectUri (where pasteRedirectUri is assigned) to normalize the base URL
from getWebUrl() (or use the URL constructor) so there is exactly one slash
between the base and PASTE_REDIRECT_PATH; i.e., strip any trailing '/' from
getWebUrl() or build the redirect with new URL(PASTE_REDIRECT_PATH, webUrl)
before assigning pasteRedirectUri.
- Around line 329-398: The wrapper Promise that races waitForCallback and
callbacks.promptForPastedCode never rejects when the outer `signal` aborts;
modify the `onOuterAbort` handler (or add a listener inside the Promise) to call
the same `settle` routine and reject the Promise with an abort error while also
aborting `callbackController` and `pasteController`. In practice, inside the new
Promise that defines `settled/settle`, update `onOuterAbort` to call `settle(()
=> { callbackController.abort(); pasteController.abort(); reject(new
Error("aborted")); });` (or use a proper AbortError), ensuring the outer abort
triggers settling the Promise in addition to aborting the inner controllers so
`waitForCallback`/`promptForPastedCode` can't leave the Promise pending. Refer
to `callbackController`, `pasteController`, `onOuterAbort`, `waitForCallback`,
and `callbacks.promptForPastedCode` to locate the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4c56b101-0d91-4a19-b9a1-14f28587883d
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (5)
packages/cli/package.jsonpackages/cli/src/commands/auth/login/LoginUI.tsxpackages/cli/src/commands/auth/login/command.tspackages/cli/src/commands/auth/login/copyToClipboard.tspackages/cli/src/lib/auth.ts
There was a problem hiding this comment.
🧹 Nitpick comments (2)
packages/cli/src/commands/auth/login/command.ts (1)
25-38: 💤 Low valueConsider non-null typing for
pasteResolve/pasteReject.These are assigned synchronously inside the
Promiseexecutor before any consumer can run, so the| nullunion and the?.()calls on lines 35-36 / 82 are defensive against an impossible state. A smalllet resolve!: …pattern (or capturing them viaPromise.withResolvers()) removes the noise without changing behavior.♻️ Optional cleanup
- let pasteResolve: ((code: string) => void) | null = null; - let pasteReject: ((err: Error) => void) | null = null; - const pastePromise = new Promise<string>((resolve, reject) => { - pasteResolve = resolve; - pasteReject = reject; - }); + let pasteResolve!: (code: string) => void; + let pasteReject!: (err: Error) => void; + const pastePromise = new Promise<string>((resolve, reject) => { + pasteResolve = resolve; + pasteReject = reject; + });Then drop the
?.on lines 35-36 and 82.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/cli/src/commands/auth/login/command.ts` around lines 25 - 38, The pasteResolve/pasteReject variables are unnecessarily typed as nullable even though they’re assigned synchronously in the Promise executor; change their declarations to non-null asserted locals (e.g., declare pasteResolve and pasteReject with the appropriate function types using the !/definite assignment pattern) so you can remove the | null unions and the safe-call operators; keep the Promise creation (pastePromise) and the assignments inside its executor, and update usages in LoginUIProps (onSubmit/onCancel) to call pasteResolve(...) and pasteReject(...) directly without ?.().packages/cli/src/commands/auth/login/LoginUI.tsx (1)
31-70: 💤 Low valueMinor: input is gated on
statusbut not onurl === null.When
status === "starting"(initial render beforeonAuthorizationUrlfires),useInputstill accepts typed characters intovalue(lines 66-69) andusePastestill appends pasted content (lines 72-75), but the cursor (showCursor = status === "waiting", line 77) is hidden, so the user gets no visual feedback that their keystrokes are being captured. Consider also showing the cursor in"starting", or buffering input only after the URL arrives.♻️ Suggested tweak
- const showCursor = status === "waiting"; + const showCursor = status === "waiting" || status === "starting";🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/cli/src/commands/auth/login/LoginUI.tsx` around lines 31 - 70, The input handlers accept keystrokes before an authorization URL is ready; update the gating so input/paste are only processed after the URL arrives (or make the cursor visible during "starting"). Specifically, in the useInput callback (and the corresponding usePaste handler), add a guard that returns early when url === null || status === "starting" (instead of only checking status "exchanging"/"done"), and/or change showCursor's expression (currently showCursor = status === "waiting") to include "starting" (e.g. status === "waiting" || status === "starting") so users see feedback; touch symbols: useInput, usePaste, showCursor, url, status, onAuthorizationUrl, setValue, onCopy to implement this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/cli/src/commands/auth/login/command.ts`:
- Around line 25-38: The pasteResolve/pasteReject variables are unnecessarily
typed as nullable even though they’re assigned synchronously in the Promise
executor; change their declarations to non-null asserted locals (e.g., declare
pasteResolve and pasteReject with the appropriate function types using the
!/definite assignment pattern) so you can remove the | null unions and the
safe-call operators; keep the Promise creation (pastePromise) and the
assignments inside its executor, and update usages in LoginUIProps
(onSubmit/onCancel) to call pasteResolve(...) and pasteReject(...) directly
without ?.().
In `@packages/cli/src/commands/auth/login/LoginUI.tsx`:
- Around line 31-70: The input handlers accept keystrokes before an
authorization URL is ready; update the gating so input/paste are only processed
after the URL arrives (or make the cursor visible during "starting").
Specifically, in the useInput callback (and the corresponding usePaste handler),
add a guard that returns early when url === null || status === "starting"
(instead of only checking status "exchanging"/"done"), and/or change
showCursor's expression (currently showCursor = status === "waiting") to include
"starting" (e.g. status === "waiting" || status === "starting") so users see
feedback; touch symbols: useInput, usePaste, showCursor, url, status,
onAuthorizationUrl, setValue, onCopy to implement this change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9363a470-8130-4a39-a725-3181269503e4
📒 Files selected for processing (3)
packages/cli/src/commands/auth/login/LoginUI.tsxpackages/cli/src/commands/auth/login/command.tspackages/cli/src/lib/auth.ts
- auth.ts: outer signal now rejects the wrapper Promise.race instead of just aborting children, so Ctrl-C from the parent can no longer hang waiting on a Winner that nobody will resolve. - auth.ts: build paste redirect via `new URL()` so a trailing slash on `SUPERSET_WEB_URL` doesn't corrupt the registered redirect URI. - command.ts: skip the Ink UI in CI even with an allocated PTY. - command.ts: drop the duplicate `p.intro` after Ink unmount — the component already rendered the header. - LoginUI.tsx: footer now reads "Esc / Ctrl+C to cancel".
Two CLI changes since v0.2.7, both fixing remote-auth UX: - `superset auth login` now races the loopback callback and the paste prompt in parallel, with an Ink UI for the paste path. Fixes login on remote machines where SSH_CONNECTION isn't set (tmux reattach, VS Code Remote, Codespaces, mosh). `c` copies the auth URL via OSC 52 with native `pbcopy`/`clip`/`wl-copy`/`xclip`/`xsel` fallback. Esc / Ctrl-C exit cleanly. (#4072) - Polished the `/cli/auth/code` web page to match Claude Code's paste-code UI (single-line code box, ghost copy button, click-to- copy with selection-on-release fallback). Wired standard readline shortcuts in the CLI paste prompt: option+backspace / ctrl+w delete the previous word; cmd+backspace / ctrl+u / ctrl+k clear the buffer. (#4075) Push cli-v0.2.8 after this lands to fire the release pipeline.
* feat(cli): always-on paste fallback for auth login (Ink UI) Drop the SSH/CI env-var sniff that gated the paste-code flow. `superset auth login` now races the loopback callback and the paste prompt in parallel — both UI affordances are always live, so remote (tmux, VS Code Remote, Codespaces, mosh) sessions get a working fallback even when SSH_CONNECTION isn't set. The auth screen renders via Ink so the "URL copied" toast is a real 1.5s React-state-driven notification, not append-only log spam. `c` copies the URL via OSC 52 (cross-device, works over SSH) with native pbcopy/clip/wl-copy/xclip as a local fallback. Esc / Ctrl-C now exit cleanly with "Login interrupted" instead of throwing. * fix(cli): address PR review on auth login Ink flow - auth.ts: outer signal now rejects the wrapper Promise.race instead of just aborting children, so Ctrl-C from the parent can no longer hang waiting on a Winner that nobody will resolve. - auth.ts: build paste redirect via `new URL()` so a trailing slash on `SUPERSET_WEB_URL` doesn't corrupt the registered redirect URI. - command.ts: skip the Ink UI in CI even with an allocated PTY. - command.ts: drop the duplicate `p.intro` after Ink unmount — the component already rendered the header. - LoginUI.tsx: footer now reads "Esc / Ctrl+C to cancel". --------- Co-authored-by: Town Hall <codetown@Town-Hall.local>
Two CLI changes since v0.2.7, both fixing remote-auth UX: - `superset auth login` now races the loopback callback and the paste prompt in parallel, with an Ink UI for the paste path. Fixes login on remote machines where SSH_CONNECTION isn't set (tmux reattach, VS Code Remote, Codespaces, mosh). `c` copies the auth URL via OSC 52 with native `pbcopy`/`clip`/`wl-copy`/`xclip`/`xsel` fallback. Esc / Ctrl-C exit cleanly. (#4072) - Polished the `/cli/auth/code` web page to match Claude Code's paste-code UI (single-line code box, ghost copy button, click-to- copy with selection-on-release fallback). Wired standard readline shortcuts in the CLI paste prompt: option+backspace / ctrl+w delete the previous word; cmd+backspace / ctrl+u / ctrl+k clear the buffer. (#4075) Push cli-v0.2.8 after this lands to fire the release pipeline.
Summary
superset auth loginnow races the loopback callback and the paste prompt in parallel — both UI affordances are always live, no env-var sniffing. Fixes login on remote machines whereSSH_CONNECTIONisn't set (tmux reattach, VS Code Remote, Codespaces, mosh).URL copied to clipboardtoast is a real 1.5s React-state-driven notification (no log spam on repeatedcpresses).ccopies the URL via OSC 52 — works cross-device over SSH because the escape passes through to the local terminal emulator. Nativepbcopy/clip/wl-copy/xclip/xselstays as a fallback for local users whose terminal blocks OSC 52.Login interruptedinstead of throwing.Test plan
Authorized!prints, token written to config.<code>#<state>back; paste path wins, loopback server closes cleanly.SSH_CONNECTION): same UI shown, paste path completes successfully.ckeybinding: presscat the empty paste prompt; verify URL is on local clipboard (pbpasteon darwin, or paste into a local browser if testing over SSH); toast appears for 1.5s and disappears with no layout drift.Login interruptedand exit 0, no stack trace.--organization <slug>non-interactive flow still works.Summary by cubic
Makes
superset auth loginreliable on remote terminals by racing the loopback callback and paste flow with a newinkUI. Adds cross-device clipboard copy and clean cancellation; non‑TTY and CI behavior stay the same, fixes Ctrl‑C hangs, and improves redirect URL handling.New Features
inkUI with a live prompt and a 1.5s “URL copied” toast; presscto copy the URL via OSC 52 with nativepbcopy/clip/wl-copy/xclip/xselfallback.inkand use the plain Clack prompts.Bug Fixes
new URL()to avoid bad URIs whenSUPERSET_WEB_URLhas a trailing slash.Written for commit c049520. Summary will update on new commits.
Summary by CodeRabbit
New Features
Dependencies