Files
pi-perplexity/src/commands/login.ts
T
Ivan Pereira 39d977c403 refactor(auth): route browser paste login through /perplexity-login
The perplexity_search tool no longer prompts for pasted browser
credentials mid-search; /perplexity-login --browser is the single
entry point for that flow. Drop the now-unused promptForBrowserAuth
option and its Cloudflare-challenge fallback from authenticate().
2026-07-14 03:26:00 +01:00

136 lines
4.2 KiB
TypeScript

import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { browserLoginInstructions } from "../auth/browser.js";
import { authenticate, saveBrowserAuthInput } from "../auth/login.js";
import { clearToken } from "../auth/storage.js";
import { AuthError } from "../search/types.js";
import { errorMessage } from "../render/util.js";
const LOGIN_COMMAND_NAME = "perplexity-login";
interface ParsedCommandArgs {
forceRefresh: boolean;
browserAuth: boolean;
showHelp: boolean;
unknown: string[];
}
function parseCommandArgs(args: string): ParsedCommandArgs {
const tokens = args
.split(/\s+/)
.map((token) => token.trim())
.filter((token) => token.length > 0);
let forceRefresh = false;
let browserAuth = false;
let showHelp = false;
const unknown: string[] = [];
for (const token of tokens) {
if (token === "--force" || token === "--refresh" || token === "-f") {
forceRefresh = true;
continue;
}
if (token === "--help" || token === "-h") {
showHelp = true;
continue;
}
if (token === "--browser" || token === "--cookie" || token === "--manual") {
browserAuth = true;
continue;
}
unknown.push(token);
}
return { forceRefresh, browserAuth, showHelp, unknown };
}
function usageText(): string {
return `Usage: /${LOGIN_COMMAND_NAME} [--force] [--browser]\n\nFlags:\n --force, --refresh, -f Clear cached token before login\n --browser, --cookie Import browser auth by pasting Copy as cURL, a Cookie header, or a session token\n --help, -h Show this help`;
}
export function registerPerplexityCommands(pi: ExtensionAPI): void {
pi.registerCommand(LOGIN_COMMAND_NAME, {
description: "Authenticate Perplexity and persist token",
handler: async (args, ctx) => {
const parsed = parseCommandArgs(args);
if (parsed.showHelp) {
ctx.ui.notify(usageText(), "info");
return;
}
if (parsed.unknown.length > 0) {
ctx.ui.notify(
`Unknown arguments: ${parsed.unknown.join(" ")}\n\n${usageText()}`,
"warning",
);
return;
}
if (parsed.forceRefresh) {
await clearToken().catch(() => undefined);
}
const promptForEmail = async (): Promise<string | undefined> => {
const value = await ctx.ui.input("Perplexity email", "you@example.com");
return value?.trim() || undefined;
};
const promptForOtp = async (email: string): Promise<string | undefined> => {
const value = await ctx.ui.input(`Enter OTP sent to ${email}`, "123456");
return value?.trim() || undefined;
};
const promptForBrowserAuth = async (): Promise<string | undefined> => {
ctx.ui.notify(browserLoginInstructions(), "info");
const value = await ctx.ui.input(
"Paste copied cURL command or Cookie header",
"curl 'https://www.perplexity.ai/' -H 'Cookie: ...'",
);
return value?.trim() || undefined;
};
try {
if (parsed.browserAuth) {
const input = await promptForBrowserAuth();
if (!input) {
ctx.ui.notify("Perplexity browser login canceled.", "warning");
return;
}
await saveBrowserAuthInput(input);
ctx.ui.notify("Perplexity browser auth saved.", "info");
return;
}
await authenticate({ promptForEmail, promptForOtp });
ctx.ui.notify("Perplexity login successful. Token saved.", "info");
} catch (error) {
if (error instanceof AuthError && error.code === "NO_TOKEN") {
if (parsed.browserAuth) {
ctx.ui.notify(error.message, "warning");
return;
}
ctx.ui.notify(
"Perplexity login canceled. Re-run /perplexity-login for email + OTP, use /perplexity-login --browser, or set PI_PERPLEXITY_EMAIL/PI_PERPLEXITY_OTP/PI_PERPLEXITY_COOKIE.",
"warning",
);
return;
}
if (error instanceof AuthError) {
ctx.ui.notify(`Perplexity login failed: ${error.message}`, "error");
return;
}
ctx.ui.notify(`Perplexity login failed: ${errorMessage(error)}`, "error");
}
},
});
}