Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ updates:
groups:
development-toolchain:
dependency-type: development
ignore:
# @types/node must track the runtime floor in package.json (engines.node
# ">=24"), not the newest release. A major bump types the project against
# a Node it does not claim to support, so tsc starts accepting APIs that
# do not exist for users on the supported version — a type error that only
# surfaces at their runtime. test/migration_guard.ts asserts that pairing,
# so a major bump lands as red CI on main; #52 did exactly that and main
# stayed broken until it was reverted. Minor and patch updates still flow.
# Raise this deliberately, together with engines.node, when the floor moves.
- dependency-name: '@types/node'
update-types: ['version-update:semver-major']

- package-ecosystem: github-actions
directory: /
Expand Down
17 changes: 16 additions & 1 deletion src/core/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,22 @@ export async function pollForToken(
}
const action = classifyPoll(resp);
if (action === "ready") return resp.access_token as string;
if (action === "denied") throw new Error("authorization denied in the browser");
// Denial is RECOVERABLE, and "authorization denied in the browser" said
// neither that nor what to do next — it read like a verdict on the account.
// It also matters that a denial is not necessarily the user's own: any
// signed-in account that learns a user_code can refuse it, so a denial the
// user did not perform means their code reached someone else.
//
// Kept to ONE line on purpose: this is rendered by formatErrorLine, which
// puts its glyph on the first line only — a multi-line message loses the
// glyph on every line after the first.
if (action === "denied") {
throw new Error(
"authorization denied — nothing was authorized and no key was created. " +
"Run `aether auth login` again to retry; if you didn't deny it, " +
"someone else has your login code — don't share it.",
);
}
if (action === "expired") throw new Error("login timed out — run `aether auth login` again");
if (action === "slow_down") interval += 5;
}
Expand Down