diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ad86d7..99c9fc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,18 @@ All notable changes to bootintel-cli are documented here. Format follows [Keep a ## [Unreleased] +### Fixed +- **Every "get an API key" message pointed at a URL that 404s.** + `https://bootintel.com/settings/api-keys` does not exist and never has; + there is no `/settings` route at all. Keys live at `/dashboard/developer`. + Six places said otherwise: `doctor`, `whoami` twice, `scan --api` twice, and + `analyze --api`. Surfaced by running `bootintel doctor` on a Mac, which is + the first time anyone read that line back. +- The same messages are now correct about what to do, not just where to go. + `bootintel login` is the path for a person at a terminal and the only one + that works below Pro, since API-key auth is Pro-gated. They recommend it + first and mention keys as the CI and scripting option. + ## [0.6.0] — 2026-09-27 — applicability without sending the log New flag, no breaking change, so MINOR per the policy above. diff --git a/crates/cli/src/api/client.rs b/crates/cli/src/api/client.rs index 987eefd..de001ea 100644 --- a/crates/cli/src/api/client.rs +++ b/crates/cli/src/api/client.rs @@ -291,7 +291,7 @@ impl ScanError { "quota exhausted — see https://bootintel.com/pricing, or fall back to client-side `bootintel scan` (no --api)", ), ScanError::Unauthorized { .. } => Some( - "check $BOOTINTEL_API_KEY, or regenerate at https://bootintel.com/settings/api-keys", + "run `bootintel login` again, or regenerate a key at https://bootintel.com/dashboard/developer", ), ScanError::BadRequest { .. } => Some( "common causes: log > 256KB (server limit), or the file isn't a boot log", diff --git a/crates/cli/src/cmd/analyze.rs b/crates/cli/src/cmd/analyze.rs index 110f5e1..cfd0533 100644 --- a/crates/cli/src/cmd/analyze.rs +++ b/crates/cli/src/cmd/analyze.rs @@ -267,7 +267,7 @@ fn build_api_config(args: &Args) -> Result> { let api_key = crate::config::resolve_api_key(); if !args.preview && api_key.is_none() { bail!( - "--api requires ${API_KEY_ENV} to be set (or use --api --preview for the free anonymous quota — 3/day per IP).\n Get a key at https://bootintel.com/settings/api-keys" + "--api needs credentials. Run `bootintel login` to sign in from this terminal,\n or `--api --preview` for the free anonymous quota (3/day per IP).\n For CI and scripting, set ${API_KEY_ENV} to a key from https://bootintel.com/dashboard/developer (Pro and above)" ); } // Same plaintext-transport policy as `scan` — shared helper in diff --git a/crates/cli/src/cmd/doctor.rs b/crates/cli/src/cmd/doctor.rs index e178dc6..91562ad 100644 --- a/crates/cli/src/cmd/doctor.rs +++ b/crates/cli/src/cmd/doctor.rs @@ -233,7 +233,7 @@ fn check_api_key() -> Check { _ => Check { name: "$BOOTINTEL_API_KEY", status: Status::Warn, - detail: "unset — --api will need --preview (anonymous, 3/day per IP).\nGet a key at https://bootintel.com/settings/api-keys".into(), + detail: "unset. Run `bootintel login` to sign in from this terminal.\nOr `--api --preview` for the anonymous quota, no account needed.\nAn API key (Pro and above, for CI and scripting) comes from https://bootintel.com/dashboard/developer".into(), }, } } diff --git a/crates/cli/src/cmd/scan/api.rs b/crates/cli/src/cmd/scan/api.rs index 8705c22..9bd7823 100644 --- a/crates/cli/src/cmd/scan/api.rs +++ b/crates/cli/src/cmd/scan/api.rs @@ -25,7 +25,7 @@ pub(super) fn run_api(args: &Args, raw: &str) -> Result<()> { if !args.preview && api_key.is_none() { bail!( - "--api requires ${API_KEY_ENV} to be set (or use --api --preview for the free anonymous quota — 3/day per IP).\n Get a key at https://bootintel.com/settings/api-keys" + "--api needs credentials. Run `bootintel login` to sign in from this terminal,\n or `--api --preview` for the free anonymous quota (3/day per IP).\n For CI and scripting, an API key (Pro and above) comes from https://bootintel.com/dashboard/developer" ); } if args.preview && api_key.is_some() { @@ -177,7 +177,10 @@ pub(super) fn render_api_error(err: ScanError, preview: bool) -> Result<()> { } writeln!(e)?; writeln!(e, " Check that {API_KEY_ENV} is set to a valid key.")?; - writeln!(e, " Manage keys: https://bootintel.com/settings/api-keys")?; + writeln!( + e, + " Manage keys: https://bootintel.com/dashboard/developer" + )?; } ScanError::BadRequest { detail } => { writeln!(e, "bootintel: server rejected the request.")?; diff --git a/crates/cli/src/cmd/whoami.rs b/crates/cli/src/cmd/whoami.rs index 98afa2f..b5fa123 100644 --- a/crates/cli/src/cmd/whoami.rs +++ b/crates/cli/src/cmd/whoami.rs @@ -95,7 +95,7 @@ pub fn run(args: Args) -> Result<()> { Some(k) => k, None => { eprintln!( - "no API key found — set $BOOTINTEL_API_KEY, or `bootintel config set api_key bik_...`\n Get a key at https://bootintel.com/settings/api-keys" + "not signed in. Run `bootintel login` to sign in from this terminal.\n For CI and scripting, an API key (Pro and above) comes from https://bootintel.com/dashboard/developer" ); std::process::exit(EX_NOPERM); } @@ -114,7 +114,7 @@ pub fn run(args: Args) -> Result<()> { Err(ProbeError::Unauthorized) => { eprintln!("unauthorized — API key rejected by {base}"); eprintln!( - " regenerate at https://bootintel.com/settings/api-keys or check $BOOTINTEL_API_KEY" + " run `bootintel login` again, or regenerate a key at https://bootintel.com/dashboard/developer" ); std::process::exit(EX_NOPERM); }