perplexity-user-mcp 0.8.42 → 0.8.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/attachments.d.ts +10 -20
  2. package/dist/browser-window.d.ts +11 -0
  3. package/dist/cf-warmup.d.ts +32 -0
  4. package/dist/checks/browser.d.ts +12 -100
  5. package/dist/checks/config.d.ts +25 -91
  6. package/dist/checks/ide.d.ts +31 -89
  7. package/dist/checks/mcp.d.ts +12 -61
  8. package/dist/checks/native-deps.d.ts +46 -131
  9. package/dist/checks/network.d.ts +13 -71
  10. package/dist/checks/probe.d.ts +37 -92
  11. package/dist/checks/probe.mjs +29 -0
  12. package/dist/checks/profiles.d.ts +13 -99
  13. package/dist/checks/profiles.mjs +35 -0
  14. package/dist/checks/runtime.d.ts +24 -89
  15. package/dist/checks/vault.d.ts +13 -142
  16. package/dist/checks/vault.mjs +6 -11
  17. package/dist/{chunk-WDIW33DA.mjs → chunk-3LUO5ATM.mjs} +1 -1
  18. package/dist/{chunk-HNSPNCFH.mjs → chunk-6CAXNBDD.mjs} +1 -1
  19. package/dist/{chunk-B65IJQZJ.mjs → chunk-6E6XTHTG.mjs} +1 -1
  20. package/dist/{chunk-S677V2JU.mjs → chunk-C5I7KXHK.mjs} +32 -2
  21. package/dist/{chunk-RK4EBZJ3.mjs → chunk-D2ZQGKHM.mjs} +11 -8
  22. package/dist/{chunk-TDXETAQT.mjs → chunk-DKEJZ4FI.mjs} +1 -1
  23. package/dist/{chunk-U7QPUNRH.mjs → chunk-DXR6EEZH.mjs} +26 -7
  24. package/dist/{chunk-HJIXH6CL.mjs → chunk-E3GRJXXJ.mjs} +2 -0
  25. package/dist/{chunk-C3HPFFTD.mjs → chunk-GBHPJ7I7.mjs} +136 -48
  26. package/dist/{chunk-D254EFYB.mjs → chunk-GBI2U336.mjs} +1 -1
  27. package/dist/chunk-GPUGKWXH.mjs +17 -0
  28. package/dist/chunk-KVV3JBSN.mjs +32 -0
  29. package/dist/{chunk-XTRJSV72.mjs → chunk-LGH5BSUY.mjs} +1 -1
  30. package/dist/{chunk-KJFX2ZXR.mjs → chunk-NMKNEEZB.mjs} +1 -1
  31. package/dist/{chunk-T6ARJK2P.mjs → chunk-P6YOLJ5T.mjs} +6 -6
  32. package/dist/{chunk-452DK6OS.mjs → chunk-QXYMYCHC.mjs} +2 -2
  33. package/dist/{chunk-Z4OLYVB2.mjs → chunk-SCZQCV7M.mjs} +1 -1
  34. package/dist/{chunk-FKQ3HP4Q.mjs → chunk-TIWHN4IW.mjs} +1 -1
  35. package/dist/{chunk-V4U3JM4R.mjs → chunk-TSLRTZYR.mjs} +1 -1
  36. package/dist/{chunk-DQQISMYN.mjs → chunk-V4LHDNWJ.mjs} +2 -2
  37. package/dist/{chunk-2FPGJKCA.mjs → chunk-YD25G5AD.mjs} +2 -2
  38. package/dist/cli.d.ts +14 -1317
  39. package/dist/cli.mjs +14 -21
  40. package/dist/client.d.ts +39 -24
  41. package/dist/client.mjs +9 -6
  42. package/dist/cloud-sync.d.ts +65 -42
  43. package/dist/cloud-sync.mjs +9 -8
  44. package/dist/config.d.ts +35 -39
  45. package/dist/config.mjs +3 -3
  46. package/dist/cookie-jar.d.ts +77 -0
  47. package/dist/daemon/attach.d.ts +5 -12
  48. package/dist/daemon/attach.mjs +18 -17
  49. package/dist/daemon/audit.d.ts +5 -7
  50. package/dist/daemon/audit.mjs +2 -2
  51. package/dist/daemon/client-http.d.ts +10 -16
  52. package/dist/daemon/client-http.mjs +18 -17
  53. package/dist/daemon/index.d.ts +17 -14
  54. package/dist/daemon/index.mjs +19 -18
  55. package/dist/daemon/install-tunnel.d.ts +8 -34
  56. package/dist/daemon/install-tunnel.mjs +2 -2
  57. package/dist/daemon/launcher.d.ts +24 -29
  58. package/dist/daemon/launcher.mjs +17 -16
  59. package/dist/daemon/local-tokens.d.ts +23 -0
  60. package/dist/daemon/lockfile.d.ts +10 -12
  61. package/dist/daemon/lockfile.mjs +2 -2
  62. package/dist/daemon/oauth-consent-cache.d.ts +86 -0
  63. package/dist/daemon/oauth-provider.d.ts +132 -0
  64. package/dist/daemon/public-pages.d.ts +9 -0
  65. package/dist/daemon/security.d.ts +52 -0
  66. package/dist/daemon/server.d.ts +12 -83
  67. package/dist/daemon/server.mjs +12 -11
  68. package/dist/daemon/token.d.ts +7 -9
  69. package/dist/daemon/token.mjs +2 -2
  70. package/dist/daemon/tunnel-providers/cloudflared-named-setup.d.ts +140 -0
  71. package/dist/daemon/tunnel-providers/cloudflared-named.d.ts +45 -0
  72. package/dist/daemon/tunnel-providers/cloudflared-quick.d.ts +8 -0
  73. package/dist/daemon/tunnel-providers/index.d.ts +16 -327
  74. package/dist/daemon/tunnel-providers/index.mjs +3 -3
  75. package/dist/daemon/tunnel-providers/ngrok-config.d.ts +18 -0
  76. package/dist/daemon/tunnel-providers/ngrok.d.ts +68 -0
  77. package/dist/daemon/tunnel-providers/types.d.ts +56 -0
  78. package/dist/daemon/tunnel.d.ts +5 -7
  79. package/dist/debug-tracer.d.ts +2 -0
  80. package/dist/doctor-report.d.ts +17 -22
  81. package/dist/doctor.d.ts +12 -44
  82. package/dist/doctor.mjs +2 -2
  83. package/dist/export.d.ts +11 -18
  84. package/dist/export.mjs +4 -4
  85. package/dist/format.d.ts +52 -0
  86. package/dist/fs-utils.d.ts +44 -0
  87. package/dist/health-check.d.ts +1 -108
  88. package/dist/health-check.mjs +3 -3
  89. package/dist/history-store.d.ts +29 -65
  90. package/dist/history-store.mjs +2 -2
  91. package/dist/impit-login-runner.d.ts +1 -469
  92. package/dist/impit-login-runner.mjs +4 -4
  93. package/dist/index.d.ts +25 -149
  94. package/dist/index.mjs +23 -20
  95. package/dist/is-main-module.d.ts +9 -0
  96. package/dist/login-runner.d.ts +1 -333
  97. package/dist/login-runner.mjs +18 -38
  98. package/dist/login.d.ts +5 -0
  99. package/dist/logout.d.ts +2 -28
  100. package/dist/logout.mjs +3 -2
  101. package/dist/manual-login-runner.d.ts +1 -150
  102. package/dist/manual-login-runner.mjs +11 -11
  103. package/dist/{native-deps-IE4B55EL.mjs → native-deps-FCSYDL4W.mjs} +4 -4
  104. package/dist/native-deps.d.ts +36 -0
  105. package/dist/package-version.d.ts +1 -0
  106. package/dist/profiles.d.ts +41 -41
  107. package/dist/profiles.mjs +1 -1
  108. package/dist/prompts.d.ts +2 -0
  109. package/dist/redact.d.ts +14 -142
  110. package/dist/refresh.d.ts +11 -16
  111. package/dist/refresh.mjs +4 -4
  112. package/dist/reinit-watcher.d.ts +15 -24
  113. package/dist/reinit-watcher.mjs +2 -2
  114. package/dist/resources.d.ts +5 -0
  115. package/dist/safe-write.d.ts +16 -0
  116. package/dist/session-metadata.d.ts +45 -0
  117. package/dist/tool-config.d.ts +10 -0
  118. package/dist/tools.d.ts +23 -0
  119. package/dist/tty-prompt.d.ts +18 -34
  120. package/dist/vault.d.ts +114 -34
  121. package/dist/vault.mjs +6 -4
  122. package/dist/viewer-detect.d.ts +2 -4
  123. package/dist/viewers.d.ts +13 -18
  124. package/dist/viewers.mjs +1 -1
  125. package/package.json +2 -2
  126. package/dist/cloud-sync.d-Cqt6y18U.d.ts +0 -42
  127. package/dist/doctor.d-CXmUqOXX.d.ts +0 -43
  128. package/dist/history-store.d-BzjBF2m3.d.ts +0 -65
  129. package/dist/native-deps-BNThFHxa.d.ts +0 -175
  130. package/dist/profiles.d-DqS1oZWr.d.ts +0 -41
  131. package/dist/session-metadata-B9aV_n5g.d.ts +0 -148
  132. package/dist/vault.d-BSJWDLhp.d.ts +0 -37
  133. package/dist/viewer-detect.d-HWGnyFAA.d.ts +0 -4
  134. package/dist/viewers.d-BGCK6sw6.d.ts +0 -10
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  CATEGORIES,
4
4
  formatReportMarkdown,
5
5
  runAll
6
- } from "./chunk-HNSPNCFH.mjs";
6
+ } from "./chunk-6CAXNBDD.mjs";
7
7
  import {
8
8
  buildIssueBody,
9
9
  buildIssueUrl,
@@ -12,15 +12,15 @@ import {
12
12
  } from "./chunk-DPGMKSSA.mjs";
13
13
  import {
14
14
  attachToDaemon
15
- } from "./chunk-Z4OLYVB2.mjs";
15
+ } from "./chunk-SCZQCV7M.mjs";
16
16
  import {
17
17
  ensureDaemon,
18
18
  startDaemon
19
- } from "./chunk-RK4EBZJ3.mjs";
20
- import "./chunk-XTRJSV72.mjs";
19
+ } from "./chunk-D2ZQGKHM.mjs";
20
+ import "./chunk-LGH5BSUY.mjs";
21
21
  import "./chunk-6YMQVLFX.mjs";
22
- import "./chunk-FKQ3HP4Q.mjs";
23
- import "./chunk-KJFX2ZXR.mjs";
22
+ import "./chunk-TIWHN4IW.mjs";
23
+ import "./chunk-NMKNEEZB.mjs";
24
24
  import {
25
25
  buildAnswerPreview,
26
26
  buildHistoryBody,
@@ -35,27 +35,31 @@ import {
35
35
  registerTools,
36
36
  saveToolConfig,
37
37
  watchToolConfig
38
- } from "./chunk-T6ARJK2P.mjs";
39
- import "./chunk-V4U3JM4R.mjs";
40
- import "./chunk-TDXETAQT.mjs";
38
+ } from "./chunk-P6YOLJ5T.mjs";
39
+ import "./chunk-TSLRTZYR.mjs";
40
+ import "./chunk-DKEJZ4FI.mjs";
41
41
  import {
42
42
  watchActiveProfile,
43
43
  watchReinit
44
- } from "./chunk-WDIW33DA.mjs";
44
+ } from "./chunk-3LUO5ATM.mjs";
45
+ import {
46
+ isMainModule
47
+ } from "./chunk-GPUGKWXH.mjs";
45
48
  import "./chunk-HMKLWVXB.mjs";
46
49
  import {
47
50
  hydrateCloudHistoryEntry,
48
51
  syncCloudHistory
49
- } from "./chunk-2FPGJKCA.mjs";
52
+ } from "./chunk-YD25G5AD.mjs";
50
53
  import {
51
54
  PerplexityClient
52
- } from "./chunk-C3HPFFTD.mjs";
55
+ } from "./chunk-GBHPJ7I7.mjs";
56
+ import "./chunk-KVV3JBSN.mjs";
53
57
  import {
54
58
  getImpitRuntimeDir,
55
59
  getModelsCacheInfo,
56
60
  isImpitAvailable,
57
61
  refreshAccountInfo
58
- } from "./chunk-DQQISMYN.mjs";
62
+ } from "./chunk-V4LHDNWJ.mjs";
59
63
  import {
60
64
  append,
61
65
  countAll,
@@ -75,24 +79,23 @@ import {
75
79
  tag,
76
80
  update,
77
81
  upsertFromCloud
78
- } from "./chunk-B65IJQZJ.mjs";
82
+ } from "./chunk-6E6XTHTG.mjs";
79
83
  import {
80
84
  exportThread
81
- } from "./chunk-D254EFYB.mjs";
85
+ } from "./chunk-GBI2U336.mjs";
82
86
  import {
83
87
  findBrowser
84
- } from "./chunk-U7QPUNRH.mjs";
88
+ } from "./chunk-DXR6EEZH.mjs";
85
89
  import {
86
90
  getUnsealMaterial
87
- } from "./chunk-S677V2JU.mjs";
91
+ } from "./chunk-C5I7KXHK.mjs";
88
92
  import "./chunk-MTDFKNXX.mjs";
89
93
  import {
90
94
  getActiveName
91
- } from "./chunk-HJIXH6CL.mjs";
95
+ } from "./chunk-E3GRJXXJ.mjs";
92
96
  import "./chunk-4UEJOM6W.mjs";
93
97
 
94
98
  // src/index.ts
95
- import { pathToFileURL } from "url";
96
99
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
97
100
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
98
101
  var client;
@@ -228,7 +231,7 @@ async function main() {
228
231
  await shutdownClientWithTimeout(client);
229
232
  }
230
233
  }
231
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
234
+ if (isMainModule(import.meta.url)) {
232
235
  runEntrypoint().catch(async (error) => {
233
236
  console.error("[perplexity-mcp] Fatal error:", error);
234
237
  await shutdownClientWithTimeout(client);
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Returns true when the importing module is the process entrypoint (i.e. was
3
+ * invoked as `node <script>` or via a bin symlink), false when it was imported
4
+ * by another module.
5
+ *
6
+ * @param {string} metaUrl - The caller's `import.meta.url`
7
+ * @returns {boolean}
8
+ */
9
+ export function isMainModule(metaUrl: string): boolean;
@@ -1,333 +1 @@
1
- import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
- import { chromium } from 'patchright';
3
- import { Vault } from './vault.d-BSJWDLhp.js';
4
- import { resolveBrowserExecutable } from './config.js';
5
- import { recordLoginSuccess, getActiveName, getProfilePaths } from './profiles.d-DqS1oZWr.js';
6
- import { redact } from './redact.js';
7
- import { c as collectSessionMetadata, b as buildRuntimeEndpoints, p as pageRequest } from './session-metadata-B9aV_n5g.js';
8
-
9
- async function minimizePageWindow(page) {
10
- try {
11
- const context = page?.context?.();
12
- if (!context || typeof context.newCDPSession !== "function") return false;
13
- const session = await context.newCDPSession(page);
14
- try {
15
- const { windowId } = await session.send("Browser.getWindowForTarget");
16
- await session.send("Browser.setWindowBounds", {
17
- windowId,
18
- bounds: { windowState: "minimized" },
19
- });
20
- return true;
21
- } finally {
22
- await session.detach().catch(() => {});
23
- }
24
- } catch {
25
- return false;
26
- }
27
- }
28
-
29
- // Auto-OTP login runner. Parent provides email via PERPLEXITY_EMAIL; we
30
- // drive the real Perplexity email+OTP flow (NextAuth on the live site,
31
- // legacy /login/* on the local mock), prompt for the six-digit code via
32
- // IPC, and persist the resulting session into the profile vault.
33
-
34
-
35
- const ORIGIN = process.env.PERPLEXITY_ORIGIN || "https://www.perplexity.ai";
36
- const LOGIN_PATH = process.env.PERPLEXITY_LOGIN_PATH || "/account";
37
- const EMAIL = process.env.PERPLEXITY_EMAIL;
38
- const OTP_TIMEOUT_MS = Number(process.env.PERPLEXITY_OTP_TIMEOUT_MS ?? 300_000);
39
- const CF_TIMEOUT_MS = Number(process.env.PERPLEXITY_CF_TIMEOUT_MS ?? 20_000);
40
- const MAX_RETRIES = 2;
41
-
42
- function resolveProfile() {
43
- return process.env.PERPLEXITY_PROFILE || getActiveName() || "default";
44
- }
45
-
46
- function isLocalOrigin(origin) {
47
- return /^https?:\/\/(127\.0\.0\.1|localhost)(:\d+)?(?:\/|$)/i.test(origin);
48
- }
49
-
50
- function ipc(msg) { if (process.send) process.send(msg); }
51
- function emit(obj) { process.stdout.write(JSON.stringify(obj) + "\n"); }
52
-
53
- async function awaitOtp() {
54
- return new Promise((resolve, reject) => {
55
- const timer = setTimeout(() => {
56
- process.removeListener("message", handler);
57
- reject(new Error("otp_timeout"));
58
- }, OTP_TIMEOUT_MS);
59
- const handler = (m) => {
60
- if (m && typeof m.otp === "string") {
61
- clearTimeout(timer);
62
- process.removeListener("message", handler);
63
- resolve(m.otp);
64
- }
65
- };
66
- process.on("message", handler);
67
- });
68
- }
69
-
70
- async function main() {
71
- if (!EMAIL) {
72
- emit({ ok: false, reason: "no_email" });
73
- process.exit(1);
74
- }
75
-
76
- const PROFILE = resolveProfile();
77
- const localOrigin = isLocalOrigin(ORIGIN);
78
-
79
- let executablePath;
80
- let channel;
81
- if (!localOrigin) {
82
- try {
83
- ({ path: executablePath, channel } = await resolveBrowserExecutable());
84
- } catch (err) {
85
- emit({ ok: false, reason: "chrome_missing", error: redact(String(err?.message ?? err)) });
86
- process.exit(4);
87
- }
88
- }
89
-
90
- const browser = await chromium.launch({
91
- headless: localOrigin,
92
- ...(executablePath ? { executablePath } : {}),
93
- ...(channel && ["chrome", "msedge", "chromium"].includes(channel) ? { channel } : {}),
94
- args: localOrigin ? [] : ["--start-minimized"],
95
- });
96
- const ctx = await browser.newContext({ ignoreHTTPSErrors: true });
97
- const page = await ctx.newPage();
98
-
99
- try {
100
- await page.goto(`${ORIGIN}${LOGIN_PATH}`, { waitUntil: "domcontentloaded", timeout: 30_000 });
101
- } catch {}
102
- if (!localOrigin) await minimizePageWindow(page);
103
-
104
- const ready = await waitForLoginReady(page);
105
- if (!ready) {
106
- const title = await page.title().catch(() => "");
107
- await browser.close().catch(() => {});
108
- emit({ ok: false, reason: /just a moment/i.test(title) ? "cf_blocked" : "auto_unsupported" });
109
- process.exit(/just a moment/i.test(title) ? 3 : 2);
110
- }
111
-
112
- const liveAttempt = await startLiveEmailFlow(page);
113
- let authFlow = liveAttempt;
114
- if (liveAttempt.kind === "unsupported") {
115
- authFlow = await startLegacyMockFlow(page);
116
- }
117
- if (!localOrigin) await minimizePageWindow(page);
118
-
119
- if (authFlow.kind === "sso_required") {
120
- await browser.close().catch(() => {});
121
- emit({ ok: false, reason: "sso_required" });
122
- process.exit(2);
123
- }
124
-
125
- if (authFlow.kind === "unsupported") {
126
- await browser.close().catch(() => {});
127
- emit({ ok: false, reason: "auto_unsupported", detail: authFlow.detail });
128
- process.exit(2);
129
- }
130
-
131
- if (authFlow.kind === "email_rejected") {
132
- await browser.close().catch(() => {});
133
- emit({ ok: false, reason: "email_rejected", detail: authFlow.detail });
134
- process.exit(2);
135
- }
136
-
137
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
138
- ipc({ phase: "awaiting_otp", attempt });
139
- let otp;
140
- try {
141
- otp = await awaitOtp();
142
- } catch {
143
- await browser.close().catch(() => {});
144
- emit({ ok: false, reason: "otp_timeout" });
145
- process.exit(2);
146
- }
147
-
148
- const submitResp = await submitOtp(page, authFlow, otp);
149
- if (submitResp.ok) {
150
- const allCookies = await ctx.cookies();
151
- const metadata = await collectSessionMetadata(page, ORIGIN, {
152
- sessionData: submitResp.sessionData,
153
- sessionTimeoutMs: 10_000,
154
- });
155
-
156
- const vault = new Vault();
157
- await vault.set(PROFILE, "cookies", JSON.stringify(allCookies));
158
- if (metadata.sessionData?.user?.email) await vault.set(PROFILE, "email", metadata.sessionData.user.email);
159
- if (metadata.sessionData?.user?.id) await vault.set(PROFILE, "userId", metadata.sessionData.user.id);
160
-
161
- const paths = getProfilePaths(PROFILE);
162
- if (!existsSync(paths.dir)) mkdirSync(paths.dir, { recursive: true });
163
- writeFileSync(paths.modelsCache, JSON.stringify(metadata.cache, null, 2));
164
- recordLoginSuccess(PROFILE, { tier: metadata.tier, loginMode: "auto", lastLogin: new Date().toISOString() });
165
- writeFileSync(paths.reinit, String(Date.now()));
166
-
167
- await browser.close().catch(() => {});
168
- emit({ ok: true, tier: metadata.tier, modelCount: Object.keys(metadata.models?.models ?? {}).length });
169
- process.exit(0);
170
- }
171
-
172
- if (authFlow.kind === "live") {
173
- await page.goto(authFlow.verifyUrl, { waitUntil: "domcontentloaded", timeout: 30_000 }).catch(() => {});
174
- if (!localOrigin) await minimizePageWindow(page);
175
- }
176
-
177
- if (attempt === MAX_RETRIES) {
178
- await browser.close().catch(() => {});
179
- emit({ ok: false, reason: "otp_rejected" });
180
- process.exit(2);
181
- }
182
- }
183
- }
184
-
185
- async function waitForLoginReady(page) {
186
- const started = Date.now();
187
- while (Date.now() - started < CF_TIMEOUT_MS) {
188
- if (await page.locator('input[type="email"]').count().catch(() => 0)) {
189
- return true;
190
- }
191
- const title = await page.title().catch(() => "");
192
- if (title && !/just a moment/i.test(title)) {
193
- const body = await page.locator("body").innerText().catch(() => "");
194
- if (/continue with email/i.test(body) || /single sign-on/i.test(body) || /continue/i.test(body)) {
195
- return true;
196
- }
197
- }
198
- await page.waitForTimeout(500);
199
- }
200
- return false;
201
- }
202
-
203
- async function startLiveEmailFlow(page) {
204
- const endpoints = buildRuntimeEndpoints(ORIGIN);
205
- const csrf = await pageRequest(page, endpoints.csrf);
206
- if (!(csrf.ok && csrf.contentType.includes("json") && csrf.json?.csrfToken)) {
207
- return {
208
- kind: "unsupported",
209
- detail: { step: "csrf", status: csrf.status, contentType: csrf.contentType, error: csrf.error ?? undefined },
210
- };
211
- }
212
-
213
- const sso = await pageRequest(page, endpoints.ssoDetails, {
214
- method: "POST",
215
- headers: { "content-type": "application/json" },
216
- body: JSON.stringify({ email: EMAIL }),
217
- });
218
- if (sso.ok && sso.json?.organization) {
219
- return { kind: "sso_required" };
220
- }
221
-
222
- const redirectUrl = `${ORIGIN}/account?login-source=settings`;
223
- const signIn = await pageRequest(page, endpoints.signInEmail, {
224
- method: "POST",
225
- headers: { "content-type": "application/json" },
226
- body: JSON.stringify({
227
- email: EMAIL,
228
- useNumericOtp: "true",
229
- csrfToken: csrf.json.csrfToken,
230
- callbackUrl: `${redirectUrl}#locale=en-US`,
231
- json: "true",
232
- }),
233
- });
234
-
235
- if (!(signIn.ok && signIn.contentType.includes("json") && signIn.json?.url)) {
236
- return {
237
- kind: signIn.status >= 400 && signIn.status < 500 ? "email_rejected" : "unsupported",
238
- detail: { step: "signin_email", status: signIn.status, contentType: signIn.contentType, error: signIn.error ?? undefined },
239
- };
240
- }
241
-
242
- const verifyUrl = new URL(signIn.json.url, ORIGIN);
243
- verifyUrl.searchParams.set("email", EMAIL);
244
- verifyUrl.searchParams.set("redirectUrl", redirectUrl);
245
- await page.goto(verifyUrl.toString(), { waitUntil: "domcontentloaded", timeout: 30_000 }).catch(() => {});
246
-
247
- return {
248
- kind: "live",
249
- redirectUrl,
250
- verifyUrl: verifyUrl.toString(),
251
- };
252
- }
253
-
254
- async function startLegacyMockFlow(page) {
255
- const emailResp = await pageRequest(page, `${ORIGIN}/login/email`, {
256
- method: "POST",
257
- headers: { "content-type": "application/json" },
258
- body: JSON.stringify({ email: EMAIL }),
259
- });
260
-
261
- if (emailResp.redirected && (emailResp.url || "").includes("/sso")) {
262
- return { kind: "sso_required" };
263
- }
264
-
265
- const looksUnsupported =
266
- emailResp.status === 404 ||
267
- emailResp.status === 405 ||
268
- emailResp.status >= 500 ||
269
- !emailResp.contentType.includes("json");
270
-
271
- if (looksUnsupported) {
272
- return {
273
- kind: "unsupported",
274
- detail: { step: "legacy_email", status: emailResp.status, contentType: emailResp.contentType, error: emailResp.error ?? undefined },
275
- };
276
- }
277
-
278
- if (!emailResp.ok) {
279
- return { kind: "email_rejected", detail: emailResp.status };
280
- }
281
-
282
- return { kind: "legacy" };
283
- }
284
-
285
- async function submitOtp(page, flow, otp) {
286
- if (flow.kind === "legacy") {
287
- const submitResp = await pageRequest(page, `${ORIGIN}/login/otp`, {
288
- method: "POST",
289
- headers: { "content-type": "application/json" },
290
- body: JSON.stringify({ email: EMAIL, otp }),
291
- });
292
- if (submitResp.status !== 200) {
293
- return { ok: false };
294
- }
295
- const metadata = await collectSessionMetadata(page, ORIGIN, { sessionTimeoutMs: 2_000 });
296
- return { ok: !!metadata.sessionData?.user?.id, sessionData: metadata.sessionData };
297
- }
298
-
299
- const endpoints = buildRuntimeEndpoints(ORIGIN);
300
- const redirectResp = await pageRequest(page, endpoints.otpRedirectLink, {
301
- method: "POST",
302
- headers: { "content-type": "application/json" },
303
- body: JSON.stringify({
304
- email: EMAIL,
305
- otp,
306
- redirectUrl: flow.redirectUrl,
307
- emailLoginMethod: "web-otp",
308
- loginSource: null,
309
- }),
310
- });
311
-
312
- if (!(redirectResp.ok && redirectResp.contentType.includes("json") && redirectResp.json?.redirect)) {
313
- return { ok: false };
314
- }
315
-
316
- const callbackUrl = new URL(redirectResp.json.redirect, ORIGIN).toString();
317
- await page.goto(callbackUrl, { waitUntil: "domcontentloaded", timeout: 30_000 }).catch(() => {});
318
- const metadata = await collectSessionMetadata(page, ORIGIN, { sessionTimeoutMs: 5_000 });
319
- return { ok: !!metadata.sessionData?.user?.id, sessionData: metadata.sessionData };
320
- }
321
-
322
- main().catch((err) => {
323
- const msg = err?.message ?? err;
324
- const stack = err?.stack;
325
- emit({
326
- ok: false,
327
- reason: "crash",
328
- error: redact(String(msg ?? "unknown error")),
329
- detail: redact(String(msg ?? "unknown error")),
330
- ...(stack ? { stack: redact(String(stack)) } : {}),
331
- });
332
- process.exit(5);
333
- });
1
+ export {};
@@ -6,47 +6,27 @@ import {
6
6
  import {
7
7
  redact
8
8
  } from "./chunk-HMKLWVXB.mjs";
9
+ import {
10
+ loginLaunchArgs,
11
+ minimizePageWindow
12
+ } from "./chunk-KVV3JBSN.mjs";
9
13
  import {
10
14
  resolveBrowserExecutable
11
- } from "./chunk-U7QPUNRH.mjs";
15
+ } from "./chunk-DXR6EEZH.mjs";
12
16
  import {
13
17
  Vault
14
- } from "./chunk-S677V2JU.mjs";
18
+ } from "./chunk-C5I7KXHK.mjs";
15
19
  import "./chunk-MTDFKNXX.mjs";
16
20
  import {
17
21
  getActiveName,
18
22
  getProfilePaths,
19
23
  recordLoginSuccess
20
- } from "./chunk-HJIXH6CL.mjs";
24
+ } from "./chunk-E3GRJXXJ.mjs";
21
25
  import "./chunk-4UEJOM6W.mjs";
22
26
 
23
27
  // src/login-runner.js
24
28
  import { writeFileSync, existsSync, mkdirSync } from "fs";
25
29
  import { chromium } from "patchright";
26
-
27
- // src/browser-window.js
28
- async function minimizePageWindow(page) {
29
- try {
30
- const context = page?.context?.();
31
- if (!context || typeof context.newCDPSession !== "function") return false;
32
- const session = await context.newCDPSession(page);
33
- try {
34
- const { windowId } = await session.send("Browser.getWindowForTarget");
35
- await session.send("Browser.setWindowBounds", {
36
- windowId,
37
- bounds: { windowState: "minimized" }
38
- });
39
- return true;
40
- } finally {
41
- await session.detach().catch(() => {
42
- });
43
- }
44
- } catch {
45
- return false;
46
- }
47
- }
48
-
49
- // src/login-runner.js
50
30
  var ORIGIN = process.env.PERPLEXITY_ORIGIN || "https://www.perplexity.ai";
51
31
  var LOGIN_PATH = process.env.PERPLEXITY_LOGIN_PATH || "/account";
52
32
  var EMAIL = process.env.PERPLEXITY_EMAIL;
@@ -87,6 +67,7 @@ async function main() {
87
67
  process.exit(1);
88
68
  }
89
69
  const PROFILE = resolveProfile();
70
+ const paths = getProfilePaths(PROFILE);
90
71
  const localOrigin = isLocalOrigin(ORIGIN);
91
72
  let executablePath;
92
73
  let channel;
@@ -98,13 +79,13 @@ async function main() {
98
79
  process.exit(4);
99
80
  }
100
81
  }
101
- const browser = await chromium.launch({
82
+ const ctx = await chromium.launchPersistentContext(paths.loginBrowserData, {
102
83
  headless: localOrigin,
84
+ ignoreHTTPSErrors: true,
103
85
  ...executablePath ? { executablePath } : {},
104
86
  ...channel && ["chrome", "msedge", "chromium"].includes(channel) ? { channel } : {},
105
- args: localOrigin ? [] : ["--start-minimized"]
87
+ args: loginLaunchArgs(localOrigin)
106
88
  });
107
- const ctx = await browser.newContext({ ignoreHTTPSErrors: true });
108
89
  const page = await ctx.newPage();
109
90
  try {
110
91
  await page.goto(`${ORIGIN}${LOGIN_PATH}`, { waitUntil: "domcontentloaded", timeout: 3e4 });
@@ -114,7 +95,7 @@ async function main() {
114
95
  const ready = await waitForLoginReady(page);
115
96
  if (!ready) {
116
97
  const title = await page.title().catch(() => "");
117
- await browser.close().catch(() => {
98
+ await ctx.browser()?.close().catch(() => {
118
99
  });
119
100
  emit({ ok: false, reason: /just a moment/i.test(title) ? "cf_blocked" : "auto_unsupported" });
120
101
  process.exit(/just a moment/i.test(title) ? 3 : 2);
@@ -126,19 +107,19 @@ async function main() {
126
107
  }
127
108
  if (!localOrigin) await minimizePageWindow(page);
128
109
  if (authFlow.kind === "sso_required") {
129
- await browser.close().catch(() => {
110
+ await ctx.browser()?.close().catch(() => {
130
111
  });
131
112
  emit({ ok: false, reason: "sso_required" });
132
113
  process.exit(2);
133
114
  }
134
115
  if (authFlow.kind === "unsupported") {
135
- await browser.close().catch(() => {
116
+ await ctx.browser()?.close().catch(() => {
136
117
  });
137
118
  emit({ ok: false, reason: "auto_unsupported", detail: authFlow.detail });
138
119
  process.exit(2);
139
120
  }
140
121
  if (authFlow.kind === "email_rejected") {
141
- await browser.close().catch(() => {
122
+ await ctx.browser()?.close().catch(() => {
142
123
  });
143
124
  emit({ ok: false, reason: "email_rejected", detail: authFlow.detail });
144
125
  process.exit(2);
@@ -149,7 +130,7 @@ async function main() {
149
130
  try {
150
131
  otp = await awaitOtp();
151
132
  } catch {
152
- await browser.close().catch(() => {
133
+ await ctx.browser()?.close().catch(() => {
153
134
  });
154
135
  emit({ ok: false, reason: "otp_timeout" });
155
136
  process.exit(2);
@@ -165,12 +146,11 @@ async function main() {
165
146
  await vault.set(PROFILE, "cookies", JSON.stringify(allCookies));
166
147
  if (metadata.sessionData?.user?.email) await vault.set(PROFILE, "email", metadata.sessionData.user.email);
167
148
  if (metadata.sessionData?.user?.id) await vault.set(PROFILE, "userId", metadata.sessionData.user.id);
168
- const paths = getProfilePaths(PROFILE);
169
149
  if (!existsSync(paths.dir)) mkdirSync(paths.dir, { recursive: true });
170
150
  writeFileSync(paths.modelsCache, JSON.stringify(metadata.cache, null, 2));
171
151
  recordLoginSuccess(PROFILE, { tier: metadata.tier, loginMode: "auto", lastLogin: (/* @__PURE__ */ new Date()).toISOString() });
172
152
  writeFileSync(paths.reinit, String(Date.now()));
173
- await browser.close().catch(() => {
153
+ await ctx.browser()?.close().catch(() => {
174
154
  });
175
155
  emit({ ok: true, tier: metadata.tier, modelCount: Object.keys(metadata.models?.models ?? {}).length });
176
156
  process.exit(0);
@@ -181,7 +161,7 @@ async function main() {
181
161
  if (!localOrigin) await minimizePageWindow(page);
182
162
  }
183
163
  if (attempt === MAX_RETRIES) {
184
- await browser.close().catch(() => {
164
+ await ctx.browser()?.close().catch(() => {
185
165
  });
186
166
  emit({ ok: false, reason: "otp_rejected" });
187
167
  process.exit(2);
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Browser-based login flow for Perplexity AI.
3
+ * Opens a real browser, lets you log in, then saves full browser state automatically.
4
+ */
5
+ export {};
package/dist/logout.d.ts CHANGED
@@ -1,28 +1,2 @@
1
- import { existsSync, rmSync, writeFileSync } from 'node:fs';
2
- import { Vault } from './vault.d-BSJWDLhp.js';
3
- import { getActiveName, setActive, getProfilePaths, listProfiles, getProfile } from './profiles.d-DqS1oZWr.js';
4
-
5
- async function softLogout(name) {
6
- const vault = new Vault();
7
- await vault.delete(name, "cookies").catch(() => {});
8
- const paths = getProfilePaths(name);
9
- const meta = getProfile(name);
10
- if (meta) {
11
- delete meta.lastLogin;
12
- writeFileSync(paths.meta, JSON.stringify(meta, null, 2) + "\n");
13
- }
14
- if (existsSync(paths.dir)) writeFileSync(paths.reinit, String(Date.now()));
15
- }
16
-
17
- async function hardLogout(name) {
18
- const paths = getProfilePaths(name);
19
- if (existsSync(paths.dir)) rmSync(paths.dir, { recursive: true, force: true });
20
- if (getActiveName() === name) {
21
- const remaining = listProfiles();
22
- if (remaining.length > 0) {
23
- setActive(remaining[0].name);
24
- }
25
- }
26
- }
27
-
28
- export { hardLogout, softLogout };
1
+ export function softLogout(name: any): Promise<void>;
2
+ export function hardLogout(name: any): Promise<void>;
package/dist/logout.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Vault
3
- } from "./chunk-S677V2JU.mjs";
3
+ } from "./chunk-C5I7KXHK.mjs";
4
4
  import "./chunk-MTDFKNXX.mjs";
5
5
  import {
6
6
  createProfile,
@@ -9,7 +9,7 @@ import {
9
9
  getProfilePaths,
10
10
  listProfiles,
11
11
  setActive
12
- } from "./chunk-HJIXH6CL.mjs";
12
+ } from "./chunk-E3GRJXXJ.mjs";
13
13
  import "./chunk-4UEJOM6W.mjs";
14
14
 
15
15
  // src/logout.js
@@ -24,6 +24,7 @@ async function softLogout(name) {
24
24
  delete meta.lastLogin;
25
25
  writeFileSync(paths.meta, JSON.stringify(meta, null, 2) + "\n");
26
26
  }
27
+ if (existsSync(paths.loginBrowserData)) rmSync(paths.loginBrowserData, { recursive: true, force: true });
27
28
  if (existsSync(paths.dir)) writeFileSync(paths.reinit, String(Date.now()));
28
29
  }
29
30
  async function hardLogout(name) {