From 1fb8b5fd1d709428b4d6f6fbbe0e68bc0cfe4963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20L=C3=B3pez=20Dato?= Date: Fri, 12 Jun 2026 19:14:55 -0300 Subject: [PATCH 1/2] show api-keys admin URL in interactive login prompt --- src/commands/auth/login.ts | 17 +++++++++++------ src/core/url.ts | 4 ++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/commands/auth/login.ts b/src/commands/auth/login.ts index 6639c6b..5c15c44 100644 --- a/src/commands/auth/login.ts +++ b/src/commands/auth/login.ts @@ -5,7 +5,7 @@ import { DEFAULT_PROFILE, writeProfile } from "../../core/auth/storage"; import { verifyCredentials } from "../../core/auth/verify"; import { explicitProfileName, readEnvCredentials } from "../../core/config"; import { ConfigError, errorMessage } from "../../core/errors"; -import { normalizeUrl } from "../../core/url"; +import { joinUrl, normalizeUrl } from "../../core/url"; import type { ResourceView } from "../../domain/view"; import { warn } from "../../output/notice"; import { promptPassword, promptText } from "../../output/prompt"; @@ -65,7 +65,7 @@ export default defineMetabaseCommand({ } const url = await resolveUrl(args.url, env.url); - const apiKey = await resolveApiKey(args.apiKey, env.apiKey); + const apiKey = await resolveApiKey(args.apiKey, env.apiKey, url); let email: string | null = null; let authenticated = false; @@ -117,7 +117,11 @@ async function resolveUrl(flagUrl: string | undefined, envUrl: string | null): P return promptForUrl(); } -async function resolveApiKey(flagKey: string | undefined, envKey: string | null): Promise { +async function resolveApiKey( + flagKey: string | undefined, + envKey: string | null, + url: string, +): Promise { if (flagKey) { return flagKey; } @@ -128,7 +132,7 @@ async function resolveApiKey(flagKey: string | undefined, envKey: string | null) if (envKey) { return envKey; } - return promptForApiKey(); + return promptForApiKey(url); } async function promptForUrl(): Promise { @@ -155,14 +159,15 @@ async function promptForUrl(): Promise { return normalizeUrl(value); } -async function promptForApiKey(): Promise { +async function promptForApiKey(url: string): Promise { if (!process.stdin.isTTY) { throw new ConfigError( "--api-key, piped stdin, or METABASE_API_KEY required when stdin is not a TTY", ); } + const apiKeysUrl = joinUrl(url, "/admin/settings/authentication/api-keys"); return promptPassword({ - message: "API key", + message: `API key (create one at ${apiKeysUrl})`, mask: "•", validate: (input) => (input ? undefined : "API key is required"), }); diff --git a/src/core/url.ts b/src/core/url.ts index 0866035..3a146ef 100644 --- a/src/core/url.ts +++ b/src/core/url.ts @@ -13,6 +13,10 @@ export function originOnly(input: string): string { return parsed.origin; } +export function joinUrl(base: string, path: string): string { + return new URL(path, base).href; +} + export function localUrl(port: number): string { return `http://localhost:${port}`; } From 84331123684d91692b2d2cfb06a682a7a353027b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20L=C3=B3pez=20Dato?= Date: Fri, 12 Jun 2026 19:46:08 -0300 Subject: [PATCH 2/2] dash not parentheses --- src/commands/auth/login.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/auth/login.ts b/src/commands/auth/login.ts index f2a75b7..07ce794 100644 --- a/src/commands/auth/login.ts +++ b/src/commands/auth/login.ts @@ -197,7 +197,7 @@ async function chooseLoginMethod( async function promptForApiKey(url: string): Promise { return promptPassword({ - message: `API key (create one at ${url}/admin/settings/authentication/api-keys)`, + message: `API key - create one at ${url}/admin/settings/authentication/api-keys`, mask: "•", validate: (input) => (input ? undefined : "API key is required"), });