perplexity-user-mcp 0.8.36

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 (125) hide show
  1. package/README.md +192 -0
  2. package/dist/attachments.d.ts +20 -0
  3. package/dist/attachments.mjs +43 -0
  4. package/dist/checks/browser.d.ts +100 -0
  5. package/dist/checks/browser.mjs +89 -0
  6. package/dist/checks/config.d.ts +91 -0
  7. package/dist/checks/config.mjs +88 -0
  8. package/dist/checks/ide.d.ts +89 -0
  9. package/dist/checks/ide.mjs +80 -0
  10. package/dist/checks/mcp.d.ts +61 -0
  11. package/dist/checks/mcp.mjs +56 -0
  12. package/dist/checks/native-deps.d.ts +131 -0
  13. package/dist/checks/native-deps.mjs +115 -0
  14. package/dist/checks/network.d.ts +71 -0
  15. package/dist/checks/network.mjs +70 -0
  16. package/dist/checks/probe.d.ts +93 -0
  17. package/dist/checks/probe.mjs +82 -0
  18. package/dist/checks/profiles.d.ts +99 -0
  19. package/dist/checks/profiles.mjs +90 -0
  20. package/dist/checks/runtime.d.ts +89 -0
  21. package/dist/checks/runtime.mjs +90 -0
  22. package/dist/checks/vault.d.ts +101 -0
  23. package/dist/checks/vault.mjs +90 -0
  24. package/dist/chunk-3B276PGG.mjs +115 -0
  25. package/dist/chunk-4UEJOM6W.mjs +9 -0
  26. package/dist/chunk-6EP2BLTV.mjs +205 -0
  27. package/dist/chunk-6YMQVLFX.mjs +146 -0
  28. package/dist/chunk-7JL36EBH.mjs +118 -0
  29. package/dist/chunk-DPGMKSSA.mjs +57 -0
  30. package/dist/chunk-H4BUAPPO.mjs +1950 -0
  31. package/dist/chunk-HMKLWVXB.mjs +109 -0
  32. package/dist/chunk-HTUAQRKH.mjs +125 -0
  33. package/dist/chunk-HU5B4FXS.mjs +139 -0
  34. package/dist/chunk-KCXM2M4B.mjs +1006 -0
  35. package/dist/chunk-LKJMLGFP.mjs +237 -0
  36. package/dist/chunk-LZPLNZ5U.mjs +67 -0
  37. package/dist/chunk-MTDFKNXX.mjs +19 -0
  38. package/dist/chunk-OF4DMAPJ.mjs +511 -0
  39. package/dist/chunk-PE23RMXY.mjs +43 -0
  40. package/dist/chunk-Q2VY4R5F.mjs +175 -0
  41. package/dist/chunk-S5VD7WTU.mjs +2540 -0
  42. package/dist/chunk-SVPRB62V.mjs +106 -0
  43. package/dist/chunk-TQLCLE4L.mjs +345 -0
  44. package/dist/chunk-U3DGFLXZ.mjs +43 -0
  45. package/dist/chunk-X45O6YD3.mjs +688 -0
  46. package/dist/chunk-XKSWCEGI.mjs +168 -0
  47. package/dist/chunk-Z7DAACGZ.mjs +534 -0
  48. package/dist/chunk-ZQFUZPLO.mjs +257 -0
  49. package/dist/cli.d.ts +952 -0
  50. package/dist/cli.mjs +827 -0
  51. package/dist/client.d.ts +355 -0
  52. package/dist/client.mjs +27 -0
  53. package/dist/cloud-sync.d-Cqt6y18U.d.ts +42 -0
  54. package/dist/cloud-sync.d.ts +42 -0
  55. package/dist/cloud-sync.mjs +17 -0
  56. package/dist/config.d.ts +186 -0
  57. package/dist/config.mjs +54 -0
  58. package/dist/daemon/attach.d.ts +36 -0
  59. package/dist/daemon/attach.mjs +25 -0
  60. package/dist/daemon/audit.d.ts +23 -0
  61. package/dist/daemon/audit.mjs +12 -0
  62. package/dist/daemon/client-http.d.ts +42 -0
  63. package/dist/daemon/client-http.mjs +29 -0
  64. package/dist/daemon/index.d.ts +14 -0
  65. package/dist/daemon/index.mjs +110 -0
  66. package/dist/daemon/install-tunnel.d.ts +46 -0
  67. package/dist/daemon/install-tunnel.mjs +14 -0
  68. package/dist/daemon/launcher.d.ts +163 -0
  69. package/dist/daemon/launcher.mjs +50 -0
  70. package/dist/daemon/lockfile.d.ts +29 -0
  71. package/dist/daemon/lockfile.mjs +18 -0
  72. package/dist/daemon/server.d.ts +159 -0
  73. package/dist/daemon/server.mjs +20 -0
  74. package/dist/daemon/token.d.ts +17 -0
  75. package/dist/daemon/token.mjs +17 -0
  76. package/dist/daemon/tunnel-providers/index.d.ts +330 -0
  77. package/dist/daemon/tunnel-providers/index.mjs +57 -0
  78. package/dist/daemon/tunnel.d.ts +23 -0
  79. package/dist/daemon/tunnel.mjs +9 -0
  80. package/dist/doctor-report.d.ts +24 -0
  81. package/dist/doctor-report.mjs +14 -0
  82. package/dist/doctor.d-CXmUqOXX.d.ts +43 -0
  83. package/dist/doctor.d.ts +44 -0
  84. package/dist/doctor.mjs +16 -0
  85. package/dist/export.d.ts +19 -0
  86. package/dist/export.mjs +15 -0
  87. package/dist/health-check.d.ts +108 -0
  88. package/dist/health-check.mjs +92 -0
  89. package/dist/history-store.d-BzjBF2m3.d.ts +65 -0
  90. package/dist/history-store.d.ts +65 -0
  91. package/dist/history-store.mjs +48 -0
  92. package/dist/impit-login-runner.d.ts +469 -0
  93. package/dist/impit-login-runner.mjs +685 -0
  94. package/dist/index.d.ts +159 -0
  95. package/dist/index.mjs +236 -0
  96. package/dist/login-runner.d.ts +333 -0
  97. package/dist/login-runner.mjs +320 -0
  98. package/dist/logout.d.ts +28 -0
  99. package/dist/logout.mjs +45 -0
  100. package/dist/manual-login-runner.d.ts +150 -0
  101. package/dist/manual-login-runner.mjs +146 -0
  102. package/dist/native-deps-BNThFHxa.d.ts +175 -0
  103. package/dist/native-deps-YNKXITRY.mjs +139 -0
  104. package/dist/profiles.d-DqS1oZWr.d.ts +41 -0
  105. package/dist/profiles.d.ts +41 -0
  106. package/dist/profiles.mjs +33 -0
  107. package/dist/redact.d.ts +159 -0
  108. package/dist/redact.mjs +11 -0
  109. package/dist/refresh.d.ts +118 -0
  110. package/dist/refresh.mjs +21 -0
  111. package/dist/reinit-watcher.d.ts +15 -0
  112. package/dist/reinit-watcher.mjs +8 -0
  113. package/dist/session-metadata-B9aV_n5g.d.ts +148 -0
  114. package/dist/tty-prompt.d.ts +44 -0
  115. package/dist/tty-prompt.mjs +39 -0
  116. package/dist/vault.d-BtRSLZiM.d.ts +8 -0
  117. package/dist/vault.d.ts +37 -0
  118. package/dist/vault.mjs +21 -0
  119. package/dist/viewer-detect.d-HWGnyFAA.d.ts +4 -0
  120. package/dist/viewer-detect.d.ts +4 -0
  121. package/dist/viewer-detect.mjs +37 -0
  122. package/dist/viewers.d-BGCK6sw6.d.ts +10 -0
  123. package/dist/viewers.d.ts +18 -0
  124. package/dist/viewers.mjs +122 -0
  125. package/package.json +152 -0
@@ -0,0 +1,45 @@
1
+ import {
2
+ Vault
3
+ } from "./chunk-TQLCLE4L.mjs";
4
+ import "./chunk-MTDFKNXX.mjs";
5
+ import {
6
+ createProfile,
7
+ getActiveName,
8
+ getProfile,
9
+ getProfilePaths,
10
+ listProfiles,
11
+ setActive
12
+ } from "./chunk-XKSWCEGI.mjs";
13
+ import "./chunk-4UEJOM6W.mjs";
14
+
15
+ // src/logout.js
16
+ import { existsSync, writeFileSync, rmSync } from "fs";
17
+ async function softLogout(name) {
18
+ const vault = new Vault();
19
+ await vault.delete(name, "cookies").catch(() => {
20
+ });
21
+ const paths = getProfilePaths(name);
22
+ const meta = getProfile(name);
23
+ if (meta) {
24
+ delete meta.lastLogin;
25
+ writeFileSync(paths.meta, JSON.stringify(meta, null, 2) + "\n");
26
+ }
27
+ if (existsSync(paths.dir)) writeFileSync(paths.reinit, String(Date.now()));
28
+ }
29
+ async function hardLogout(name) {
30
+ const paths = getProfilePaths(name);
31
+ if (existsSync(paths.dir)) rmSync(paths.dir, { recursive: true, force: true });
32
+ if (getActiveName() === name) {
33
+ const remaining = listProfiles();
34
+ if (remaining.length > 0) {
35
+ setActive(remaining[0].name);
36
+ } else {
37
+ createProfile("default");
38
+ setActive("default");
39
+ }
40
+ }
41
+ }
42
+ export {
43
+ hardLogout,
44
+ softLogout
45
+ };
@@ -0,0 +1,150 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { chromium } from 'patchright';
3
+ import { Vault } from './vault.d-BtRSLZiM.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 } from './session-metadata-B9aV_n5g.js';
8
+
9
+ // Headed login runner. User drives the browser; we poll cookies until the
10
+ // session token appears, then write the vault + meta + models-cache +
11
+ // .reinit sentinel, and exit.
12
+
13
+
14
+ const ORIGIN = process.env.PERPLEXITY_ORIGIN || "https://www.perplexity.ai";
15
+ // Perplexity's login flow lives at /account (the `/login` path doesn't exist
16
+ // on www.perplexity.ai). Integration tests override via env var to point at
17
+ // the mock server's /login route.
18
+ const LOGIN_PATH = process.env.PERPLEXITY_LOGIN_PATH || "/account";
19
+ const POLL_MS = Number(process.env.PERPLEXITY_POLL_MS ?? 2000);
20
+ const MAX_WAIT_MS = 180_000;
21
+ const CF_TIMEOUT_MS = Number(process.env.PERPLEXITY_CF_TIMEOUT_MS ?? 20_000);
22
+
23
+ function resolveProfile() {
24
+ return process.env.PERPLEXITY_PROFILE || getActiveName() || "default";
25
+ }
26
+
27
+ const isTest = !!process.env.PERPLEXITY_TEST_AUTO_LOGIN_EMAIL || !!process.env.PERPLEXITY_TEST_BROWSER_CLOSE_AFTER_MS;
28
+
29
+ function ipc(msg) { if (process.send) process.send(msg); }
30
+ function emit(obj) { process.stdout.write(JSON.stringify(obj) + "\n"); }
31
+
32
+ async function main() {
33
+ const PROFILE = resolveProfile();
34
+
35
+ let executablePath;
36
+ let channel;
37
+ if (!isTest) {
38
+ try {
39
+ ({ path: executablePath, channel } = await resolveBrowserExecutable());
40
+ } catch (err) {
41
+ emit({ ok: false, reason: "chrome_missing", error: redact(String(err?.message ?? err)) });
42
+ process.exit(4);
43
+ }
44
+ }
45
+
46
+ const browser = await chromium.launch({
47
+ headless: isTest,
48
+ ...(executablePath ? { executablePath } : {}),
49
+ ...(channel && ["chrome", "msedge", "chromium"].includes(channel) ? { channel } : {}),
50
+ }); // isTest = headless in CI; humans see headed.
51
+ const ctx = await browser.newContext({ ignoreHTTPSErrors: true });
52
+ const page = await ctx.newPage();
53
+
54
+ let cfClosed = false;
55
+ browser.on("disconnected", () => { cfClosed = true; });
56
+
57
+ // Navigate to the login page so the page has a same-origin context for
58
+ // subsequent credentialed fetch() calls. Going to ORIGIN's root path can
59
+ // land on a marketing page that may not set a usable document origin for
60
+ // in-page fetch. Path is env-var-configurable for the mock server tests.
61
+ try { await page.goto(`${ORIGIN}${LOGIN_PATH}`, { waitUntil: "domcontentloaded", timeout: 30_000 }); }
62
+ catch { /* continue; next phase checks CF */ }
63
+ if (!isTest) await page.bringToFront().catch(() => {});
64
+
65
+ // CF resolve check
66
+ const cfStart = Date.now();
67
+ while (Date.now() - cfStart < CF_TIMEOUT_MS) {
68
+ try {
69
+ const title = await page.title();
70
+ if (!/just a moment/i.test(title)) break;
71
+ } catch { break; }
72
+ await page.waitForTimeout(500);
73
+ }
74
+ if (Date.now() - cfStart >= CF_TIMEOUT_MS) {
75
+ const title = await page.title().catch(() => "");
76
+ if (/just a moment/i.test(title)) {
77
+ await browser.close().catch(() => {});
78
+ emit({ ok: false, reason: "cf_blocked" });
79
+ process.exit(3);
80
+ }
81
+ }
82
+
83
+ ipc({ phase: "awaiting_user" });
84
+ if (!isTest) await page.bringToFront().catch(() => {});
85
+
86
+ // Test hook: auto-drive the login so CI doesn't need a human.
87
+ if (process.env.PERPLEXITY_TEST_AUTO_LOGIN_EMAIL) {
88
+ const email = process.env.PERPLEXITY_TEST_AUTO_LOGIN_EMAIL;
89
+ await page.evaluate(async ({ origin, email }) => {
90
+ await fetch(`${origin}/login/email`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ email }) });
91
+ await fetch(`${origin}/login/otp`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ email, otp: "123456" }) });
92
+ }, { origin: ORIGIN, email });
93
+ }
94
+
95
+ // Test hook: force a browser close to exercise the cancelled path.
96
+ if (process.env.PERPLEXITY_TEST_BROWSER_CLOSE_AFTER_MS) {
97
+ setTimeout(() => browser.close().catch(() => {}), Number(process.env.PERPLEXITY_TEST_BROWSER_CLOSE_AFTER_MS));
98
+ }
99
+
100
+ const started = Date.now();
101
+ let sessionCookie = null;
102
+ while (Date.now() - started < MAX_WAIT_MS) {
103
+ if (cfClosed) {
104
+ emit({ ok: false, reason: "cancelled" });
105
+ process.exit(2);
106
+ }
107
+ await new Promise((r) => setTimeout(r, POLL_MS));
108
+ const cookies = await ctx.cookies().catch(() => []);
109
+ sessionCookie = cookies.find((c) => c.name === "__Secure-next-auth.session-token");
110
+ if (sessionCookie) break;
111
+ }
112
+ if (!sessionCookie) {
113
+ await browser.close().catch(() => {});
114
+ emit({ ok: false, reason: "timeout" });
115
+ process.exit(2);
116
+ }
117
+
118
+ const allCookies = await ctx.cookies();
119
+ const metadata = await collectSessionMetadata(page, ORIGIN, { sessionTimeoutMs: 10_000 });
120
+
121
+ const vault = new Vault();
122
+ await vault.set(PROFILE, "cookies", JSON.stringify(allCookies));
123
+ if (metadata.sessionData?.user?.email) await vault.set(PROFILE, "email", metadata.sessionData.user.email);
124
+ if (metadata.sessionData?.user?.id) await vault.set(PROFILE, "userId", metadata.sessionData.user.id);
125
+
126
+ const paths = getProfilePaths(PROFILE);
127
+ if (!existsSync(paths.dir)) mkdirSync(paths.dir, { recursive: true });
128
+ writeFileSync(paths.modelsCache, JSON.stringify(metadata.cache, null, 2));
129
+
130
+ recordLoginSuccess(PROFILE, { tier: metadata.tier, loginMode: "manual", lastLogin: new Date().toISOString() });
131
+
132
+ writeFileSync(paths.reinit, String(Date.now()));
133
+
134
+ await browser.close().catch(() => {});
135
+ emit({ ok: true, tier: metadata.tier, modelCount: Object.keys(metadata.models?.models ?? {}).length });
136
+ process.exit(0);
137
+ }
138
+
139
+ main().catch((err) => {
140
+ const msg = err?.message ?? err;
141
+ const stack = err?.stack;
142
+ emit({
143
+ ok: false,
144
+ reason: "crash",
145
+ error: redact(String(msg ?? "unknown error")),
146
+ detail: redact(String(msg ?? "unknown error")),
147
+ ...(stack ? { stack: redact(String(stack)) } : {}),
148
+ });
149
+ process.exit(5);
150
+ });
@@ -0,0 +1,146 @@
1
+ import {
2
+ collectSessionMetadata
3
+ } from "./chunk-HU5B4FXS.mjs";
4
+ import {
5
+ redact
6
+ } from "./chunk-HMKLWVXB.mjs";
7
+ import {
8
+ resolveBrowserExecutable
9
+ } from "./chunk-LKJMLGFP.mjs";
10
+ import {
11
+ Vault
12
+ } from "./chunk-TQLCLE4L.mjs";
13
+ import "./chunk-MTDFKNXX.mjs";
14
+ import {
15
+ getActiveName,
16
+ getProfilePaths,
17
+ recordLoginSuccess
18
+ } from "./chunk-XKSWCEGI.mjs";
19
+ import "./chunk-4UEJOM6W.mjs";
20
+
21
+ // src/manual-login-runner.js
22
+ import { writeFileSync, existsSync, mkdirSync } from "fs";
23
+ import { chromium } from "patchright";
24
+ var ORIGIN = process.env.PERPLEXITY_ORIGIN || "https://www.perplexity.ai";
25
+ var LOGIN_PATH = process.env.PERPLEXITY_LOGIN_PATH || "/account";
26
+ var POLL_MS = Number(process.env.PERPLEXITY_POLL_MS ?? 2e3);
27
+ var MAX_WAIT_MS = 18e4;
28
+ var CF_TIMEOUT_MS = Number(process.env.PERPLEXITY_CF_TIMEOUT_MS ?? 2e4);
29
+ function resolveProfile() {
30
+ return process.env.PERPLEXITY_PROFILE || getActiveName() || "default";
31
+ }
32
+ var isTest = !!process.env.PERPLEXITY_TEST_AUTO_LOGIN_EMAIL || !!process.env.PERPLEXITY_TEST_BROWSER_CLOSE_AFTER_MS;
33
+ function ipc(msg) {
34
+ if (process.send) process.send(msg);
35
+ }
36
+ function emit(obj) {
37
+ process.stdout.write(JSON.stringify(obj) + "\n");
38
+ }
39
+ async function main() {
40
+ const PROFILE = resolveProfile();
41
+ let executablePath;
42
+ let channel;
43
+ if (!isTest) {
44
+ try {
45
+ ({ path: executablePath, channel } = await resolveBrowserExecutable());
46
+ } catch (err) {
47
+ emit({ ok: false, reason: "chrome_missing", error: redact(String(err?.message ?? err)) });
48
+ process.exit(4);
49
+ }
50
+ }
51
+ const browser = await chromium.launch({
52
+ headless: isTest,
53
+ ...executablePath ? { executablePath } : {},
54
+ ...channel && ["chrome", "msedge", "chromium"].includes(channel) ? { channel } : {}
55
+ });
56
+ const ctx = await browser.newContext({ ignoreHTTPSErrors: true });
57
+ const page = await ctx.newPage();
58
+ let cfClosed = false;
59
+ browser.on("disconnected", () => {
60
+ cfClosed = true;
61
+ });
62
+ try {
63
+ await page.goto(`${ORIGIN}${LOGIN_PATH}`, { waitUntil: "domcontentloaded", timeout: 3e4 });
64
+ } catch {
65
+ }
66
+ if (!isTest) await page.bringToFront().catch(() => {
67
+ });
68
+ const cfStart = Date.now();
69
+ while (Date.now() - cfStart < CF_TIMEOUT_MS) {
70
+ try {
71
+ const title = await page.title();
72
+ if (!/just a moment/i.test(title)) break;
73
+ } catch {
74
+ break;
75
+ }
76
+ await page.waitForTimeout(500);
77
+ }
78
+ if (Date.now() - cfStart >= CF_TIMEOUT_MS) {
79
+ const title = await page.title().catch(() => "");
80
+ if (/just a moment/i.test(title)) {
81
+ await browser.close().catch(() => {
82
+ });
83
+ emit({ ok: false, reason: "cf_blocked" });
84
+ process.exit(3);
85
+ }
86
+ }
87
+ ipc({ phase: "awaiting_user" });
88
+ if (!isTest) await page.bringToFront().catch(() => {
89
+ });
90
+ if (process.env.PERPLEXITY_TEST_AUTO_LOGIN_EMAIL) {
91
+ const email = process.env.PERPLEXITY_TEST_AUTO_LOGIN_EMAIL;
92
+ await page.evaluate(async ({ origin, email: email2 }) => {
93
+ await fetch(`${origin}/login/email`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ email: email2 }) });
94
+ await fetch(`${origin}/login/otp`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ email: email2, otp: "123456" }) });
95
+ }, { origin: ORIGIN, email });
96
+ }
97
+ if (process.env.PERPLEXITY_TEST_BROWSER_CLOSE_AFTER_MS) {
98
+ setTimeout(() => browser.close().catch(() => {
99
+ }), Number(process.env.PERPLEXITY_TEST_BROWSER_CLOSE_AFTER_MS));
100
+ }
101
+ const started = Date.now();
102
+ let sessionCookie = null;
103
+ while (Date.now() - started < MAX_WAIT_MS) {
104
+ if (cfClosed) {
105
+ emit({ ok: false, reason: "cancelled" });
106
+ process.exit(2);
107
+ }
108
+ await new Promise((r) => setTimeout(r, POLL_MS));
109
+ const cookies = await ctx.cookies().catch(() => []);
110
+ sessionCookie = cookies.find((c) => c.name === "__Secure-next-auth.session-token");
111
+ if (sessionCookie) break;
112
+ }
113
+ if (!sessionCookie) {
114
+ await browser.close().catch(() => {
115
+ });
116
+ emit({ ok: false, reason: "timeout" });
117
+ process.exit(2);
118
+ }
119
+ const allCookies = await ctx.cookies();
120
+ const metadata = await collectSessionMetadata(page, ORIGIN, { sessionTimeoutMs: 1e4 });
121
+ const vault = new Vault();
122
+ await vault.set(PROFILE, "cookies", JSON.stringify(allCookies));
123
+ if (metadata.sessionData?.user?.email) await vault.set(PROFILE, "email", metadata.sessionData.user.email);
124
+ if (metadata.sessionData?.user?.id) await vault.set(PROFILE, "userId", metadata.sessionData.user.id);
125
+ const paths = getProfilePaths(PROFILE);
126
+ if (!existsSync(paths.dir)) mkdirSync(paths.dir, { recursive: true });
127
+ writeFileSync(paths.modelsCache, JSON.stringify(metadata.cache, null, 2));
128
+ recordLoginSuccess(PROFILE, { tier: metadata.tier, loginMode: "manual", lastLogin: (/* @__PURE__ */ new Date()).toISOString() });
129
+ writeFileSync(paths.reinit, String(Date.now()));
130
+ await browser.close().catch(() => {
131
+ });
132
+ emit({ ok: true, tier: metadata.tier, modelCount: Object.keys(metadata.models?.models ?? {}).length });
133
+ process.exit(0);
134
+ }
135
+ main().catch((err) => {
136
+ const msg = err?.message ?? err;
137
+ const stack = err?.stack;
138
+ emit({
139
+ ok: false,
140
+ reason: "crash",
141
+ error: redact(String(msg ?? "unknown error")),
142
+ detail: redact(String(msg ?? "unknown error")),
143
+ ...stack ? { stack: redact(String(stack)) } : {}
144
+ });
145
+ process.exit(5);
146
+ });
@@ -0,0 +1,175 @@
1
+ import { existsSync, readFileSync, rmSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { spawn } from 'node:child_process';
3
+ import { join } from 'node:path';
4
+ import { getImpitRuntimeDir } from './refresh.js';
5
+ import './config.js';
6
+ import 'patchright';
7
+
8
+ // Speed Boost (impit) install / uninstall helpers shared by the CLI and
9
+ // the VS Code extension. Mirrors the behavior of
10
+ // packages/extension/src/native-deps.ts but lives in the npm package so
11
+ // `npx perplexity-user-mcp install-speed-boost` doesn't require the
12
+ // extension to be installed.
13
+
14
+
15
+ const STATE_MARKER = "native-deps-state.json";
16
+
17
+ function stateFile() {
18
+ return join(getImpitRuntimeDir(), STATE_MARKER);
19
+ }
20
+
21
+ function writeState(state) {
22
+ const dir = getImpitRuntimeDir();
23
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
24
+ writeFileSync(stateFile(), JSON.stringify(state, null, 2));
25
+ }
26
+
27
+ function readState() {
28
+ const f = stateFile();
29
+ if (!existsSync(f)) return null;
30
+ try {
31
+ return JSON.parse(readFileSync(f, "utf8"));
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Ensure a minimal package.json exists in the runtime dir so `npm install`
39
+ * lands there cleanly (prevents npm walking up to the user's home and
40
+ * polluting a parent package.json). Mirrors the extension's helper.
41
+ */
42
+ function ensureRuntimePackageJson() {
43
+ const dir = getImpitRuntimeDir();
44
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
45
+ const pkgPath = join(dir, "package.json");
46
+ if (!existsSync(pkgPath)) {
47
+ writeFileSync(
48
+ pkgPath,
49
+ JSON.stringify(
50
+ {
51
+ name: "perplexity-native-deps",
52
+ version: "1.0.0",
53
+ private: true,
54
+ description: "Runtime native dependencies for the Perplexity MCP CLI/extension.",
55
+ },
56
+ null,
57
+ 2,
58
+ ),
59
+ );
60
+ }
61
+ return dir;
62
+ }
63
+
64
+ /**
65
+ * @returns {{ installed: boolean; version: string | null; installedAt: string | null; runtimeDir: string }}
66
+ */
67
+ function getImpitStatus() {
68
+ const dir = getImpitRuntimeDir();
69
+ const marker = join(dir, "node_modules", "impit", "package.json");
70
+ if (!existsSync(marker)) {
71
+ return { installed: false, version: null, installedAt: null, runtimeDir: dir };
72
+ }
73
+ let version = null;
74
+ try {
75
+ version = JSON.parse(readFileSync(marker, "utf8")).version ?? null;
76
+ } catch {
77
+ // marker exists but unreadable — still count as installed
78
+ }
79
+ const state = readState();
80
+ return {
81
+ installed: true,
82
+ version,
83
+ installedAt: state?.installedAt ?? null,
84
+ runtimeDir: dir,
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Install impit into ~/.perplexity-mcp/native-deps/ via `npm install`.
90
+ * Does NOT depend on the user's project — uses --prefix into our own
91
+ * runtime dir and ensures a package.json exists there.
92
+ *
93
+ * @param {{ log?: (line: string) => void }} [opts]
94
+ * @returns {Promise<{ ok: boolean; version?: string; error?: string }>}
95
+ */
96
+ async function installImpit(opts = {}) {
97
+ const log = opts.log ?? (() => undefined);
98
+ const dir = ensureRuntimePackageJson();
99
+ log(`Installing impit into ${dir} via npm...`);
100
+
101
+ return new Promise((resolve) => {
102
+ const child = spawn("npm", ["install", "impit@latest", "--no-audit", "--no-fund", "--loglevel=error"], {
103
+ cwd: dir,
104
+ shell: process.platform === "win32",
105
+ stdio: ["ignore", "pipe", "pipe"],
106
+ });
107
+
108
+ let stderrBuf = "";
109
+
110
+ child.stdout?.on("data", (chunk) => {
111
+ for (const line of chunk.toString().split(/\r?\n/)) if (line.trim()) log(`npm: ${line}`);
112
+ });
113
+ child.stderr?.on("data", (chunk) => {
114
+ const text = chunk.toString();
115
+ stderrBuf += text;
116
+ for (const line of text.split(/\r?\n/)) if (line.trim()) log(`npm: ${line}`);
117
+ });
118
+
119
+ child.on("error", (err) => {
120
+ log(`npm spawn error: ${err.message}`);
121
+ resolve({
122
+ ok: false,
123
+ error:
124
+ err.message.includes("ENOENT")
125
+ ? "`npm` not found on PATH. Install Node.js (which ships with npm) and try again."
126
+ : err.message,
127
+ });
128
+ });
129
+
130
+ child.on("close", (code) => {
131
+ if (code !== 0) {
132
+ resolve({
133
+ ok: false,
134
+ error: `npm exited with code ${code}. stderr: ${stderrBuf.slice(0, 400) || "(empty)"}`,
135
+ });
136
+ return;
137
+ }
138
+ const status = getImpitStatus();
139
+ if (!status.installed) {
140
+ resolve({
141
+ ok: false,
142
+ error: "npm reported success but impit package.json not found in node_modules. Check npm output above.",
143
+ });
144
+ return;
145
+ }
146
+ writeState({
147
+ version: status.version ?? "unknown",
148
+ installedAt: new Date().toISOString(),
149
+ });
150
+ log(`impit ${status.version ?? ""} installed successfully.`);
151
+ resolve({ ok: true, version: status.version ?? undefined });
152
+ });
153
+ });
154
+ }
155
+
156
+ /**
157
+ * Remove the entire native-deps runtime directory.
158
+ *
159
+ * @param {{ log?: (line: string) => void }} [opts]
160
+ * @returns {{ ok: boolean; error?: string }}
161
+ */
162
+ function uninstallImpit(opts = {}) {
163
+ const log = opts.log ?? (() => undefined);
164
+ const dir = getImpitRuntimeDir();
165
+ if (!existsSync(dir)) return { ok: true };
166
+ try {
167
+ rmSync(dir, { recursive: true, force: true });
168
+ log(`Removed ${dir}.`);
169
+ return { ok: true };
170
+ } catch (err) {
171
+ return { ok: false, error: err.message };
172
+ }
173
+ }
174
+
175
+ export { getImpitStatus, installImpit, uninstallImpit };
@@ -0,0 +1,139 @@
1
+ import {
2
+ getImpitRuntimeDir
3
+ } from "./chunk-Z7DAACGZ.mjs";
4
+ import "./chunk-LKJMLGFP.mjs";
5
+ import "./chunk-TQLCLE4L.mjs";
6
+ import "./chunk-MTDFKNXX.mjs";
7
+ import "./chunk-XKSWCEGI.mjs";
8
+ import "./chunk-4UEJOM6W.mjs";
9
+
10
+ // src/native-deps.js
11
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
12
+ import { spawn } from "child_process";
13
+ import { join } from "path";
14
+ var STATE_MARKER = "native-deps-state.json";
15
+ function stateFile() {
16
+ return join(getImpitRuntimeDir(), STATE_MARKER);
17
+ }
18
+ function writeState(state) {
19
+ const dir = getImpitRuntimeDir();
20
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
21
+ writeFileSync(stateFile(), JSON.stringify(state, null, 2));
22
+ }
23
+ function readState() {
24
+ const f = stateFile();
25
+ if (!existsSync(f)) return null;
26
+ try {
27
+ return JSON.parse(readFileSync(f, "utf8"));
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+ function ensureRuntimePackageJson() {
33
+ const dir = getImpitRuntimeDir();
34
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
35
+ const pkgPath = join(dir, "package.json");
36
+ if (!existsSync(pkgPath)) {
37
+ writeFileSync(
38
+ pkgPath,
39
+ JSON.stringify(
40
+ {
41
+ name: "perplexity-native-deps",
42
+ version: "1.0.0",
43
+ private: true,
44
+ description: "Runtime native dependencies for the Perplexity MCP CLI/extension."
45
+ },
46
+ null,
47
+ 2
48
+ )
49
+ );
50
+ }
51
+ return dir;
52
+ }
53
+ function getImpitStatus() {
54
+ const dir = getImpitRuntimeDir();
55
+ const marker = join(dir, "node_modules", "impit", "package.json");
56
+ if (!existsSync(marker)) {
57
+ return { installed: false, version: null, installedAt: null, runtimeDir: dir };
58
+ }
59
+ let version = null;
60
+ try {
61
+ version = JSON.parse(readFileSync(marker, "utf8")).version ?? null;
62
+ } catch {
63
+ }
64
+ const state = readState();
65
+ return {
66
+ installed: true,
67
+ version,
68
+ installedAt: state?.installedAt ?? null,
69
+ runtimeDir: dir
70
+ };
71
+ }
72
+ async function installImpit(opts = {}) {
73
+ const log = opts.log ?? (() => void 0);
74
+ const dir = ensureRuntimePackageJson();
75
+ log(`Installing impit into ${dir} via npm...`);
76
+ return new Promise((resolve) => {
77
+ const child = spawn("npm", ["install", "impit@latest", "--no-audit", "--no-fund", "--loglevel=error"], {
78
+ cwd: dir,
79
+ shell: process.platform === "win32",
80
+ stdio: ["ignore", "pipe", "pipe"]
81
+ });
82
+ let stderrBuf = "";
83
+ child.stdout?.on("data", (chunk) => {
84
+ for (const line of chunk.toString().split(/\r?\n/)) if (line.trim()) log(`npm: ${line}`);
85
+ });
86
+ child.stderr?.on("data", (chunk) => {
87
+ const text = chunk.toString();
88
+ stderrBuf += text;
89
+ for (const line of text.split(/\r?\n/)) if (line.trim()) log(`npm: ${line}`);
90
+ });
91
+ child.on("error", (err) => {
92
+ log(`npm spawn error: ${err.message}`);
93
+ resolve({
94
+ ok: false,
95
+ error: err.message.includes("ENOENT") ? "`npm` not found on PATH. Install Node.js (which ships with npm) and try again." : err.message
96
+ });
97
+ });
98
+ child.on("close", (code) => {
99
+ if (code !== 0) {
100
+ resolve({
101
+ ok: false,
102
+ error: `npm exited with code ${code}. stderr: ${stderrBuf.slice(0, 400) || "(empty)"}`
103
+ });
104
+ return;
105
+ }
106
+ const status = getImpitStatus();
107
+ if (!status.installed) {
108
+ resolve({
109
+ ok: false,
110
+ error: "npm reported success but impit package.json not found in node_modules. Check npm output above."
111
+ });
112
+ return;
113
+ }
114
+ writeState({
115
+ version: status.version ?? "unknown",
116
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
117
+ });
118
+ log(`impit ${status.version ?? ""} installed successfully.`);
119
+ resolve({ ok: true, version: status.version ?? void 0 });
120
+ });
121
+ });
122
+ }
123
+ function uninstallImpit(opts = {}) {
124
+ const log = opts.log ?? (() => void 0);
125
+ const dir = getImpitRuntimeDir();
126
+ if (!existsSync(dir)) return { ok: true };
127
+ try {
128
+ rmSync(dir, { recursive: true, force: true });
129
+ log(`Removed ${dir}.`);
130
+ return { ok: true };
131
+ } catch (err) {
132
+ return { ok: false, error: err.message };
133
+ }
134
+ }
135
+ export {
136
+ getImpitStatus,
137
+ installImpit,
138
+ uninstallImpit
139
+ };
@@ -0,0 +1,41 @@
1
+ interface ProfileMeta {
2
+ name: string;
3
+ displayName: string;
4
+ createdAt: string;
5
+ loginMode?: string;
6
+ tier?: string;
7
+ lastLogin?: string;
8
+ }
9
+
10
+ interface ProfilePaths {
11
+ dir: string;
12
+ meta: string;
13
+ vault: string;
14
+ vaultPlain: string;
15
+ browserData: string;
16
+ modelsCache: string;
17
+ history: string;
18
+ attachments: string;
19
+ researches: string;
20
+ reinit: string;
21
+ }
22
+
23
+ declare function getConfigDir(): string;
24
+ declare function getProfilesDir(): string;
25
+ declare function getProfilePaths(name: string): ProfilePaths;
26
+ declare function validateName(name: string): string | null;
27
+ declare function createProfile(name: string, opts?: { displayName?: string; loginMode?: string }): ProfileMeta;
28
+ declare function listProfiles(): ProfileMeta[];
29
+ declare function getProfile(name: string): ProfileMeta | null;
30
+ declare function deleteProfile(name: string): void;
31
+ declare function getActiveName(): string | null;
32
+ declare function getActive(): ProfileMeta | null;
33
+ declare function setActive(name: string): void;
34
+ declare function suggestNextDefaultName(): string;
35
+ declare function renameProfile(oldName: string, newName: string): void;
36
+ declare function recordLoginSuccess(
37
+ name: string,
38
+ opts: { tier: string; loginMode: string; lastLogin: string }
39
+ ): ProfileMeta;
40
+
41
+ export { type ProfileMeta, type ProfilePaths, createProfile, deleteProfile, getActive, getActiveName, getConfigDir, getProfile, getProfilePaths, getProfilesDir, listProfiles, recordLoginSuccess, renameProfile, setActive, suggestNextDefaultName, validateName };