perplexity-user-mcp 0.8.39 → 0.8.44

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 (133) hide show
  1. package/README.md +5 -0
  2. package/dist/attachments.d.ts +10 -20
  3. package/dist/browser-window.d.ts +1 -0
  4. package/dist/cf-warmup.d.ts +32 -0
  5. package/dist/checks/browser.d.ts +12 -100
  6. package/dist/checks/config.d.ts +25 -91
  7. package/dist/checks/ide.d.ts +31 -89
  8. package/dist/checks/mcp.d.ts +12 -61
  9. package/dist/checks/native-deps.d.ts +46 -131
  10. package/dist/checks/network.d.ts +13 -71
  11. package/dist/checks/probe.d.ts +20 -92
  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-T6ARJK2P.mjs → chunk-2B5OQUXR.mjs} +6 -6
  18. package/dist/{chunk-2FPGJKCA.mjs → chunk-2EE7MNP2.mjs} +2 -2
  19. package/dist/{chunk-NJX4RBO6.mjs → chunk-2OVLCZHU.mjs} +28 -3
  20. package/dist/{chunk-WDIW33DA.mjs → chunk-3LUO5ATM.mjs} +1 -1
  21. package/dist/{chunk-B65IJQZJ.mjs → chunk-6E6XTHTG.mjs} +1 -1
  22. package/dist/{chunk-S677V2JU.mjs → chunk-C5I7KXHK.mjs} +32 -2
  23. package/dist/{chunk-TDXETAQT.mjs → chunk-DKEJZ4FI.mjs} +1 -1
  24. package/dist/{chunk-U7QPUNRH.mjs → chunk-DXR6EEZH.mjs} +26 -7
  25. package/dist/{chunk-HJIXH6CL.mjs → chunk-E3GRJXXJ.mjs} +2 -0
  26. package/dist/{chunk-RK4EBZJ3.mjs → chunk-E75J42W5.mjs} +11 -8
  27. package/dist/{chunk-452DK6OS.mjs → chunk-FNHYUE22.mjs} +2 -2
  28. package/dist/{chunk-D254EFYB.mjs → chunk-GBI2U336.mjs} +1 -1
  29. package/dist/chunk-GPUGKWXH.mjs +17 -0
  30. package/dist/{chunk-HNSPNCFH.mjs → chunk-KSNV3ZVY.mjs} +1 -1
  31. package/dist/{chunk-XTRJSV72.mjs → chunk-LGH5BSUY.mjs} +1 -1
  32. package/dist/{chunk-KJFX2ZXR.mjs → chunk-NMKNEEZB.mjs} +1 -1
  33. package/dist/{chunk-FKQ3HP4Q.mjs → chunk-TIWHN4IW.mjs} +1 -1
  34. package/dist/{chunk-V4U3JM4R.mjs → chunk-TSLRTZYR.mjs} +1 -1
  35. package/dist/{chunk-DQQISMYN.mjs → chunk-V4LHDNWJ.mjs} +2 -2
  36. package/dist/{chunk-C3HPFFTD.mjs → chunk-WHVJ724K.mjs} +84 -44
  37. package/dist/cli.d.ts +14 -1298
  38. package/dist/cli.mjs +35 -27
  39. package/dist/client.d.ts +27 -24
  40. package/dist/client.mjs +6 -6
  41. package/dist/cloud-sync.d.ts +65 -42
  42. package/dist/cloud-sync.mjs +8 -8
  43. package/dist/config.d.ts +35 -39
  44. package/dist/config.mjs +3 -3
  45. package/dist/cookie-jar.d.ts +77 -0
  46. package/dist/daemon/attach.d.ts +10 -11
  47. package/dist/daemon/attach.mjs +19 -17
  48. package/dist/daemon/audit.d.ts +5 -7
  49. package/dist/daemon/audit.mjs +2 -2
  50. package/dist/daemon/client-http.d.ts +10 -16
  51. package/dist/daemon/client-http.mjs +17 -17
  52. package/dist/daemon/index.d.ts +17 -14
  53. package/dist/daemon/index.mjs +18 -18
  54. package/dist/daemon/install-tunnel.d.ts +8 -34
  55. package/dist/daemon/install-tunnel.mjs +2 -2
  56. package/dist/daemon/launcher.d.ts +24 -29
  57. package/dist/daemon/launcher.mjs +16 -16
  58. package/dist/daemon/local-tokens.d.ts +23 -0
  59. package/dist/daemon/lockfile.d.ts +10 -12
  60. package/dist/daemon/lockfile.mjs +2 -2
  61. package/dist/daemon/oauth-consent-cache.d.ts +86 -0
  62. package/dist/daemon/oauth-provider.d.ts +132 -0
  63. package/dist/daemon/public-pages.d.ts +9 -0
  64. package/dist/daemon/security.d.ts +52 -0
  65. package/dist/daemon/server.d.ts +12 -83
  66. package/dist/daemon/server.mjs +11 -11
  67. package/dist/daemon/token.d.ts +7 -9
  68. package/dist/daemon/token.mjs +2 -2
  69. package/dist/daemon/tunnel-providers/cloudflared-named-setup.d.ts +140 -0
  70. package/dist/daemon/tunnel-providers/cloudflared-named.d.ts +45 -0
  71. package/dist/daemon/tunnel-providers/cloudflared-quick.d.ts +8 -0
  72. package/dist/daemon/tunnel-providers/index.d.ts +16 -327
  73. package/dist/daemon/tunnel-providers/index.mjs +3 -3
  74. package/dist/daemon/tunnel-providers/ngrok-config.d.ts +18 -0
  75. package/dist/daemon/tunnel-providers/ngrok.d.ts +68 -0
  76. package/dist/daemon/tunnel-providers/types.d.ts +56 -0
  77. package/dist/daemon/tunnel.d.ts +5 -7
  78. package/dist/debug-tracer.d.ts +2 -0
  79. package/dist/doctor-report.d.ts +17 -22
  80. package/dist/doctor.d.ts +12 -44
  81. package/dist/doctor.mjs +2 -2
  82. package/dist/export.d.ts +11 -18
  83. package/dist/export.mjs +4 -4
  84. package/dist/format.d.ts +52 -0
  85. package/dist/fs-utils.d.ts +8 -0
  86. package/dist/health-check.d.ts +1 -108
  87. package/dist/health-check.mjs +3 -3
  88. package/dist/history-store.d.ts +29 -65
  89. package/dist/history-store.mjs +2 -2
  90. package/dist/impit-login-runner.d.ts +1 -469
  91. package/dist/impit-login-runner.mjs +4 -4
  92. package/dist/index.d.ts +25 -149
  93. package/dist/index.mjs +22 -20
  94. package/dist/is-main-module.d.ts +9 -0
  95. package/dist/login-runner.d.ts +1 -333
  96. package/dist/login-runner.mjs +13 -13
  97. package/dist/login.d.ts +5 -0
  98. package/dist/logout.d.ts +2 -28
  99. package/dist/logout.mjs +3 -2
  100. package/dist/manual-login-runner.d.ts +1 -150
  101. package/dist/manual-login-runner.mjs +11 -11
  102. package/dist/{native-deps-IE4B55EL.mjs → native-deps-FCSYDL4W.mjs} +4 -4
  103. package/dist/native-deps.d.ts +36 -0
  104. package/dist/package-version.d.ts +1 -0
  105. package/dist/profiles.d.ts +41 -41
  106. package/dist/profiles.mjs +1 -1
  107. package/dist/prompts.d.ts +2 -0
  108. package/dist/redact.d.ts +14 -142
  109. package/dist/refresh.d.ts +11 -16
  110. package/dist/refresh.mjs +4 -4
  111. package/dist/reinit-watcher.d.ts +15 -24
  112. package/dist/reinit-watcher.mjs +2 -2
  113. package/dist/resources.d.ts +5 -0
  114. package/dist/safe-write.d.ts +16 -0
  115. package/dist/session-metadata.d.ts +45 -0
  116. package/dist/tool-config.d.ts +10 -0
  117. package/dist/tools.d.ts +23 -0
  118. package/dist/tty-prompt.d.ts +18 -34
  119. package/dist/vault.d.ts +114 -34
  120. package/dist/vault.mjs +6 -4
  121. package/dist/viewer-detect.d.ts +2 -4
  122. package/dist/viewers.d.ts +13 -18
  123. package/dist/viewers.mjs +1 -1
  124. package/package.json +3 -3
  125. package/dist/cloud-sync.d-Cqt6y18U.d.ts +0 -42
  126. package/dist/doctor.d-CXmUqOXX.d.ts +0 -43
  127. package/dist/history-store.d-BzjBF2m3.d.ts +0 -65
  128. package/dist/native-deps-BNThFHxa.d.ts +0 -175
  129. package/dist/profiles.d-DqS1oZWr.d.ts +0 -41
  130. package/dist/session-metadata-B9aV_n5g.d.ts +0 -148
  131. package/dist/vault.d-BSJWDLhp.d.ts +0 -37
  132. package/dist/viewer-detect.d-HWGnyFAA.d.ts +0 -4
  133. 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-KSNV3ZVY.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-NJX4RBO6.mjs";
15
+ } from "./chunk-2OVLCZHU.mjs";
16
16
  import {
17
17
  ensureDaemon,
18
18
  startDaemon
19
- } from "./chunk-RK4EBZJ3.mjs";
20
- import "./chunk-XTRJSV72.mjs";
19
+ } from "./chunk-E75J42W5.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,30 @@ 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-2B5OQUXR.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-2EE7MNP2.mjs";
50
53
  import {
51
54
  PerplexityClient
52
- } from "./chunk-C3HPFFTD.mjs";
55
+ } from "./chunk-WHVJ724K.mjs";
53
56
  import {
54
57
  getImpitRuntimeDir,
55
58
  getModelsCacheInfo,
56
59
  isImpitAvailable,
57
60
  refreshAccountInfo
58
- } from "./chunk-DQQISMYN.mjs";
61
+ } from "./chunk-V4LHDNWJ.mjs";
59
62
  import {
60
63
  append,
61
64
  countAll,
@@ -75,24 +78,23 @@ import {
75
78
  tag,
76
79
  update,
77
80
  upsertFromCloud
78
- } from "./chunk-B65IJQZJ.mjs";
81
+ } from "./chunk-6E6XTHTG.mjs";
79
82
  import {
80
83
  exportThread
81
- } from "./chunk-D254EFYB.mjs";
84
+ } from "./chunk-GBI2U336.mjs";
82
85
  import {
83
86
  findBrowser
84
- } from "./chunk-U7QPUNRH.mjs";
87
+ } from "./chunk-DXR6EEZH.mjs";
85
88
  import {
86
89
  getUnsealMaterial
87
- } from "./chunk-S677V2JU.mjs";
90
+ } from "./chunk-C5I7KXHK.mjs";
88
91
  import "./chunk-MTDFKNXX.mjs";
89
92
  import {
90
93
  getActiveName
91
- } from "./chunk-HJIXH6CL.mjs";
94
+ } from "./chunk-E3GRJXXJ.mjs";
92
95
  import "./chunk-4UEJOM6W.mjs";
93
96
 
94
97
  // src/index.ts
95
- import { pathToFileURL } from "url";
96
98
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
97
99
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
98
100
  var client;
@@ -228,7 +230,7 @@ async function main() {
228
230
  await shutdownClientWithTimeout(client);
229
231
  }
230
232
  }
231
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
233
+ if (isMainModule(import.meta.url)) {
232
234
  runEntrypoint().catch(async (error) => {
233
235
  console.error("[perplexity-mcp] Fatal error:", error);
234
236
  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 {};
@@ -8,16 +8,16 @@ import {
8
8
  } from "./chunk-HMKLWVXB.mjs";
9
9
  import {
10
10
  resolveBrowserExecutable
11
- } from "./chunk-U7QPUNRH.mjs";
11
+ } from "./chunk-DXR6EEZH.mjs";
12
12
  import {
13
13
  Vault
14
- } from "./chunk-S677V2JU.mjs";
14
+ } from "./chunk-C5I7KXHK.mjs";
15
15
  import "./chunk-MTDFKNXX.mjs";
16
16
  import {
17
17
  getActiveName,
18
18
  getProfilePaths,
19
19
  recordLoginSuccess
20
- } from "./chunk-HJIXH6CL.mjs";
20
+ } from "./chunk-E3GRJXXJ.mjs";
21
21
  import "./chunk-4UEJOM6W.mjs";
22
22
 
23
23
  // src/login-runner.js
@@ -87,6 +87,7 @@ async function main() {
87
87
  process.exit(1);
88
88
  }
89
89
  const PROFILE = resolveProfile();
90
+ const paths = getProfilePaths(PROFILE);
90
91
  const localOrigin = isLocalOrigin(ORIGIN);
91
92
  let executablePath;
92
93
  let channel;
@@ -98,13 +99,13 @@ async function main() {
98
99
  process.exit(4);
99
100
  }
100
101
  }
101
- const browser = await chromium.launch({
102
+ const ctx = await chromium.launchPersistentContext(paths.loginBrowserData, {
102
103
  headless: localOrigin,
104
+ ignoreHTTPSErrors: true,
103
105
  ...executablePath ? { executablePath } : {},
104
106
  ...channel && ["chrome", "msedge", "chromium"].includes(channel) ? { channel } : {},
105
107
  args: localOrigin ? [] : ["--start-minimized"]
106
108
  });
107
- const ctx = await browser.newContext({ ignoreHTTPSErrors: true });
108
109
  const page = await ctx.newPage();
109
110
  try {
110
111
  await page.goto(`${ORIGIN}${LOGIN_PATH}`, { waitUntil: "domcontentloaded", timeout: 3e4 });
@@ -114,7 +115,7 @@ async function main() {
114
115
  const ready = await waitForLoginReady(page);
115
116
  if (!ready) {
116
117
  const title = await page.title().catch(() => "");
117
- await browser.close().catch(() => {
118
+ await ctx.browser()?.close().catch(() => {
118
119
  });
119
120
  emit({ ok: false, reason: /just a moment/i.test(title) ? "cf_blocked" : "auto_unsupported" });
120
121
  process.exit(/just a moment/i.test(title) ? 3 : 2);
@@ -126,19 +127,19 @@ async function main() {
126
127
  }
127
128
  if (!localOrigin) await minimizePageWindow(page);
128
129
  if (authFlow.kind === "sso_required") {
129
- await browser.close().catch(() => {
130
+ await ctx.browser()?.close().catch(() => {
130
131
  });
131
132
  emit({ ok: false, reason: "sso_required" });
132
133
  process.exit(2);
133
134
  }
134
135
  if (authFlow.kind === "unsupported") {
135
- await browser.close().catch(() => {
136
+ await ctx.browser()?.close().catch(() => {
136
137
  });
137
138
  emit({ ok: false, reason: "auto_unsupported", detail: authFlow.detail });
138
139
  process.exit(2);
139
140
  }
140
141
  if (authFlow.kind === "email_rejected") {
141
- await browser.close().catch(() => {
142
+ await ctx.browser()?.close().catch(() => {
142
143
  });
143
144
  emit({ ok: false, reason: "email_rejected", detail: authFlow.detail });
144
145
  process.exit(2);
@@ -149,7 +150,7 @@ async function main() {
149
150
  try {
150
151
  otp = await awaitOtp();
151
152
  } catch {
152
- await browser.close().catch(() => {
153
+ await ctx.browser()?.close().catch(() => {
153
154
  });
154
155
  emit({ ok: false, reason: "otp_timeout" });
155
156
  process.exit(2);
@@ -165,12 +166,11 @@ async function main() {
165
166
  await vault.set(PROFILE, "cookies", JSON.stringify(allCookies));
166
167
  if (metadata.sessionData?.user?.email) await vault.set(PROFILE, "email", metadata.sessionData.user.email);
167
168
  if (metadata.sessionData?.user?.id) await vault.set(PROFILE, "userId", metadata.sessionData.user.id);
168
- const paths = getProfilePaths(PROFILE);
169
169
  if (!existsSync(paths.dir)) mkdirSync(paths.dir, { recursive: true });
170
170
  writeFileSync(paths.modelsCache, JSON.stringify(metadata.cache, null, 2));
171
171
  recordLoginSuccess(PROFILE, { tier: metadata.tier, loginMode: "auto", lastLogin: (/* @__PURE__ */ new Date()).toISOString() });
172
172
  writeFileSync(paths.reinit, String(Date.now()));
173
- await browser.close().catch(() => {
173
+ await ctx.browser()?.close().catch(() => {
174
174
  });
175
175
  emit({ ok: true, tier: metadata.tier, modelCount: Object.keys(metadata.models?.models ?? {}).length });
176
176
  process.exit(0);
@@ -181,7 +181,7 @@ async function main() {
181
181
  if (!localOrigin) await minimizePageWindow(page);
182
182
  }
183
183
  if (attempt === MAX_RETRIES) {
184
- await browser.close().catch(() => {
184
+ await ctx.browser()?.close().catch(() => {
185
185
  });
186
186
  emit({ ok: false, reason: "otp_rejected" });
187
187
  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) {