π fix(browse): require stored auth before entering interactive browser - #2
Merged
Merged
Conversation
- Add requireStoredAuth() in src/cli.ts, using resolveStoredAuth (not resolveAuth) so the check never triggers an interactive OAuth round-trip when PCLOUD_CLIENT_ID/_SECRET are set - Call requireStoredAuth() in the browse command's .action() before startBrowse(), since checking inside the render happens after the alternate screen is up and the error is written to a buffer torn down microseconds later - Refactor the not-authenticated message into exitNotAuthenticated() and reuse it from getAuthenticatedAPI() - Add src/browse.test.ts with runBrowseLoggedOut(), which spawns the CLI in a fresh HOME with no PCLOUD_* env vars and asserts the auth error prints while the ink-picture/TerminalInfo render-crash stack is absent
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
Nothing. No error, no browser, no clue.
Why
The check was never missing.
@kud/pcloud-ink'sbuildAPI()does exactly theright thing:
The problem is when it runs.
startBrowsemounts with{ alternateScreen: true }, so by the time thatconsole.errorfires theterminal has already switched to the alternate buffer.
process.exit(1)restoresthe primary buffer β and takes the message with it.
A correct check, a correct message, and a completely silent command.
The fix
Gate in
cli.tsbefore the dynamic import, while the primary buffer is still onscreen:
browsenow fails like every other command in the CLI:The in-component check in
@kud/pcloud-inkstays as-is β cockpit mounts thatcomponent directly, so it still needs its own guard.
Notes for review
resolveStoredAuth, notresolveAuth. The latter falls through to aninteractive OAuth browser round-trip when
PCLOUD_CLIENT_IDandPCLOUD_CLIENT_SECRETare set. A precondition check that can itself launch alogin flow is the wrong shape;
resolveStoredAuthonly asks whether a credentialis already on hand. Its own source comment notes it exists for "callers that
cannot await (React render paths, sync entrypoints)".
exitNotAuthenticated()is extracted, not duplicated.getAuthenticatedAPIand
requireStoredAuthnow share one message. ThereturningetAuthenticatedAPI's catch is load-bearing β TypeScript only treats a call asflow-terminating for specific declaration forms, and an arrow-const isn't one, so
without it the function trips TS2366.
Testing
Three tests in
src/browse.test.ts, spawning the CLI withHOMEpointed at anempty temp directory (
TokenStorereadsos.homedir()) and the credential envvars stripped.
cwdgoes to that temp directory too, sodotenv.config()cannotpick up a
.envfrom the repo root and hand the subprocess the credentials thetest is withholding.
Verified in both directions β with the fix, 3 pass; with
requireStoredAuth()commented out, 2 fail.
That second run mattered. On the first draft, only the message assertion failed
without the fix: the exit-code and alternate-screen assertions passed anyway,
because mounting the browser in a non-TTY crashes
ink-picture's terminal queryand exits non-zero for an unrelated reason. Both were rewritten β the surviving
ordering test now asserts that crash stack is absent, which is what actually
distinguishes "refused before mounting" from "died inside the render".
npm run typecheckclean. Worth noting it caught what vitest could not: spreadingprocess.envinto an object literal drops its index signature, sodelete env.PCLOUD_CLIENT_IDfailed to compile while the tests passed at runtime.