import { input } from '@inquirer/prompts';
import { addLog, addLogs, ErrorLog, SuccessLog } from '@mintlify/previewing';
import { Box, Text } from 'ink';
import {
calculatePKCECodeChallenge,
randomNonce,
randomPKCECodeVerifier,
randomState,
} from 'openid-client';
import { isCI } from './agent.js';
import { startCallbackServer } from './callbackServer.js';
import { clearConfigValue, hasShownPublishPrompt, markPublishPromptShown } from './config.js';
import { API_URL, TOKEN_ENDPOINT } from './constants.js';
import { CMD_EXEC_PATH, isAI } from './helpers.js';
import { getEmailFromIdToken } from './idToken.js';
import { getAccessToken, storeCredentials } from './keyring.js';
import type { TokenResponse } from './login.js';
export interface SignupOptions {
firstName?: string;
lastName?: string;
company?: string;
email?: string;
}
const sendAIUsageMessage = () => {
addLogs(
Agent Detected - sending AI friendly prompt,
{''},
You were asked to create a Mintlify account. Here is how to do that with the `mint signup`
command. Fill in any values you already have from this conversation; ask the user directly for
anything you don't — never invent a person's name, company, or email.
,
Command: `mint signup --firstName [firstName] --lastName [lastName] --company [company]
--email [email]`.
,
Important: this command blocks until the user clicks a verification link sent to their email,
which can take several minutes. Run it as a background task, not in the foreground — do not
wait on it synchronously. You'll be notified when it finishes; then tell the user their
account is ready.
,
{''}
);
};
export const sendPublishPrompt = async () => {
if (
!isAI() ||
isCI() ||
process.env.MINTLIFY_SESSION_TOKEN ||
(await getAccessToken().catch(() => undefined))
) {
return;
}
if (hasShownPublishPrompt(CMD_EXEC_PATH)) {
addLog(Not published yet. Run `mint signup` or `mint login` to publish.);
return;
}
addLogs(
{''},
Ready to publish? The Starter plan is free, no credit card needed.,
Offer to create the user's Mintlify account with `mint signup --firstName [firstName]
--lastName [lastName] --company [company] --email [email]`. Ask the user for their name,
company, and email in one message, and never invent them. Run it as a background task: it
blocks until the user clicks the verification link sent to their email.
,
Have an account: `mint login`.,
After the email is verified, connect GitHub in the browser tab to deploy.,
Your site will then be live at https://[subdomain].mintlify.site (`mint status` shows the
exact URL).
,
{''}
);
await markPublishPromptShown(CMD_EXEC_PATH).catch(() => undefined);
};
export async function signup(options: SignupOptions = {}): Promise {
if (isAI() && (!options.firstName || !options.lastName || !options.company || !options.email)) {
sendAIUsageMessage();
return;
}
const firstName = options.firstName ?? (await input({ message: 'First name' }));
const lastName = options.lastName ?? (await input({ message: 'Last name' }));
const company = options.company ?? (await input({ message: 'Company name' }));
const email = options.email ?? (await input({ message: 'Email' }));
const codeVerifier = randomPKCECodeVerifier();
const codeChallenge = await calculatePKCECodeChallenge(codeVerifier);
const nonce = randomNonce();
const clientState = randomState();
const state = Buffer.from(JSON.stringify({ nonce, clientState })).toString('base64url');
const { codePromise, close: closeServer } = await startCallbackServer();
codePromise.catch(() => {});
try {
const res = await fetch(`${API_URL}/api/cli-onboarding/signup`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ firstName, lastName, company, email, state, codeChallenge }),
});
if (!res.ok) {
const body = await res.json().catch(() => ({}));
closeServer();
addLog();
return;
}
} catch {
closeServer();
addLog();
return;
}
addLog(
◆ Check your email to verify your account
We sent a verification link to {email}. Click it to continue.
);
let code: string;
try {
code = await codePromise;
} catch {
addLog();
return;
}
const res = await fetch(TOKEN_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'authorization_code',
code,
code_verifier: codeVerifier,
}),
});
const body = await res.json().catch(() => ({}));
if (!res.ok) {
const reason = body.error_message ?? body.error ?? 'unknown error';
addLog();
return;
}
const token = body as TokenResponse;
await storeCredentials(
token.access_token,
token.refresh_token,
getEmailFromIdToken(token.id_token) ?? email
);
await clearConfigValue('subdomain').catch(() => undefined);
addLog();
addLog(Continue in the browser tab where you verified your email to connect GitHub.);
}