canceledAtEmailPrompt and canceledAtOtpPrompt were closure booleans set inside prompt callbacks to detect user cancellation after the fact. Fragile if authenticate ever retries prompts or changes order. In the command context both prompt functions are always provided, so AuthError(NO_TOKEN) unambiguously means the user declined to enter required input. Check the error code directly instead.
pi-perplexity
A pi extension that provides web search via a Perplexity Pro/Max subscription. Uses your existing subscription — no API credits consumed.
Installation
pi install pi-perplexity
Usage
Once installed, the agent gains a perplexity_search tool it can call automatically when it needs to look something up. You can also trigger a search by asking the agent to search for something.
Tool Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
string | yes | Search query |
recency |
string | no | Filter by age: hour, day, week, month, year |
limit |
number | no | Max sources to return (1–50) |
Slash Command
/perplexity-login # Authenticate and save token
/perplexity-login --force # Clear cached token and re-authenticate
Authentication
The extension tries two auth methods in order:
-
macOS Desktop App (automatic) — Borrows the JWT from the Perplexity macOS app if installed and signed in. Zero interaction required.
-
Email OTP (interactive fallback) — Prompts for your Perplexity email, sends a one-time code, and prompts for the code.
The token is cached at ~/.config/pi-perplexity/auth.json (permissions 0600). On HTTP 401/403, the cached token is automatically cleared so the next search re-authenticates.
Environment Variables
| Variable | Description |
|---|---|
PI_AUTH_NO_BORROW=1 |
Skip macOS desktop app token extraction |
PI_PERPLEXITY_EMAIL |
Email for OTP auth (skips interactive prompt) |
PI_PERPLEXITY_OTP |
OTP code for non-interactive auth |
How It Works
The extension calls Perplexity's internal SSE endpoint (perplexity_ask) with your subscription credentials. Responses stream as incremental events that are merged into a final result containing an answer and sources.
All queries use is_incognito: true — nothing appears in your Perplexity search history.
Output Format
The tool returns structured text the agent can reason over:
## Answer
<synthesized answer with inline citations>
## Sources
3 sources
[1] Example Article (2d ago)
https://example.com/article
Brief snippet of the source content...
[2] Another Source (5h ago)
https://example.com/other
Another snippet preview...
## Meta
Provider: perplexity (oauth)
Model: pplx_pro_upgraded
Development
Prerequisites
- Bun runtime
- pi coding agent (
@mariozechner/pi-coding-agent)
Commands
bun install # Install dev dependencies
bun test # Run tests (30 tests across 6 files)
bunx tsc --noEmit # Type check
Project Structure
src/
index.ts # Extension entry — registers tool and commands
auth/
login.ts # macOS app extraction + email OTP flow
storage.ts # Token persistence (~/.config/pi-perplexity/auth.json)
commands/
login.ts # /perplexity-login slash command
search/
types.ts # Type definitions (StreamEvent, SearchResult, errors)
client.ts # HTTP POST to SSE endpoint, event merging, result extraction
stream.ts # SSE line parser + incremental event merging
format.ts # SearchResult → LLM-readable text output
render/
call.ts # TUI component for tool call display
result.ts # TUI component for tool result display
util.ts # Shared render utilities
Runtime Dependencies
This extension depends on the bun runtime package at execution time.
Why: when pi loads extensions under Node/jiti, direct fetch to Perplexity is Cloudflare-challenged, so searches are executed through a Bun subprocess.
Everything else uses platform globals:
fetch— HTTP requestscrypto.randomUUID()— request IDsReadableStream— SSE parsingIntl.DateTimeFormat— timezone detection
Peer dependencies (@sinclair/typebox, @mariozechner/pi-tui, @mariozechner/pi-ai) are provided by pi at runtime.
Requirements
- Perplexity Pro or Max subscription
- Bun installed and available on
PATH(used by the search client when running under Node/jiti) - macOS (for desktop app token extraction) or interactive terminal (for email OTP)
License
Private — not published.