diff --git a/src/index.ts b/src/index.ts index 6db1806..e92a845 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1131,7 +1131,12 @@ const auth = program .command("auth") .description("Authentication and permissions") -auth.command("login").description("Login with Smithery").action(handleLogin) +auth + .command("login") + .description( + "Login with Smithery (non-TTY: outputs JSON with auth_url for agents)", + ) + .action(handleLogin) auth .command("logout") diff --git a/src/lib/cli-auth.ts b/src/lib/cli-auth.ts index d901418..6ed37b4 100644 --- a/src/lib/cli-auth.ts +++ b/src/lib/cli-auth.ts @@ -261,12 +261,15 @@ export async function executeCliAuthFlow( options: CliAuthOptions = {}, ): Promise { verbose(`Starting CLI auth flow with endpoint: ${SMITHERY_URL}`) + const isTTY = process.stdin.isTTY // Step 1: Create session - const sessionSpinner = yoctoSpinner({ - text: "Preparing authentication...", - color: "cyan", - }).start() + const sessionSpinner = isTTY + ? yoctoSpinner({ + text: "Preparing authentication...", + color: "cyan", + }).start() + : null let session: CliAuthSession try { @@ -278,33 +281,45 @@ export async function executeCliAuthFlow( } // Step 2: Display URL and open browser - console.log() - console.log(pc.cyan("Opening browser for authentication...")) - console.log() - console.log(pc.bold(" If your browser doesn't open, visit:")) - console.log(pc.blue(pc.underline(` ${session.authUrl}`))) - console.log() - - // Try to open browser (non-blocking) - try { - await openBrowserForAuth(session.authUrl) - } catch (error) { - // Silent failure - URL already shown above - verbose(`Browser opening failed: ${error}`) + if (isTTY) { + console.log() + console.log(pc.cyan("Opening browser for authentication...")) + console.log() + console.log(pc.bold(" If your browser doesn't open, visit:")) + console.log(pc.blue(pc.underline(` ${session.authUrl}`))) + console.log() + + // Try to open browser (non-blocking) + try { + await openBrowserForAuth(session.authUrl) + } catch (error) { + // Silent failure - URL already shown above + verbose(`Browser opening failed: ${error}`) + } + } else { + // Non-TTY: output machine-readable auth URL for agents + console.log( + JSON.stringify({ + auth_url: session.authUrl, + session_id: session.sessionId, + }), + ) } // Step 3: Poll for completion - const pollSpinner = yoctoSpinner({ - text: "Waiting for you to authorize in browser...", - color: "yellow", - }).start() + const pollSpinner = isTTY + ? yoctoSpinner({ + text: "Waiting for you to authorize in browser...", + color: "yellow", + }).start() + : null try { const apiKey = await pollForApiKey(session.sessionId, SMITHERY_URL, options) - pollSpinner.success("Authorization received") + pollSpinner?.success("Authorization received") return apiKey } catch (error) { - pollSpinner.error("Authorization failed") + pollSpinner?.error("Authorization failed") throw error } }