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
package/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # perplexity-user-mcp
2
+
3
+ Perplexity AI MCP server. Runs a persistent Chromium session via [patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright) to serve MCP tools for search, reasoning, research, and Computer mode from your existing Perplexity account.
4
+
5
+ Also available as the bundled MCP inside the [Perplexity Internal VS Code extension](https://github.com/Automations-Project/VSCode-Perplexity-MCP); this package is the same server, runnable standalone.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g perplexity-user-mcp
11
+ ```
12
+
13
+ or run on demand with `npx`:
14
+
15
+ ```bash
16
+ npx perplexity-user-mcp
17
+ ```
18
+
19
+ The server speaks MCP over stdio, so normally you point your MCP client (Claude Desktop, Cursor, Windsurf, Cline, Claude Code, Amp, Codex CLI) at the binary rather than invoking it directly.
20
+
21
+ ## First run & login
22
+
23
+ Login is interactive — Perplexity emails a one-time code that you have to paste back into the prompt. The MCP `perplexity_login` tool only returns instructions, because MCP tool calls cannot display interactive prompts. You log in either through the CLI or, if you have the VS Code extension, through its dashboard.
24
+
25
+ Quick start from a fresh machine:
26
+
27
+ ```bash
28
+ npm install -g perplexity-user-mcp
29
+ npx perplexity-user-mcp install-speed-boost # optional, strongly recommended
30
+ npx perplexity-user-mcp login --mode auto --email me@example.com
31
+ # terminal will prompt for the OTP from your email
32
+ ```
33
+
34
+ `--mode auto` runs the email + OTP flow over HTTP (impit-backed if Speed Boost is installed; otherwise driven through the browser). `--mode manual` opens a visible browser window so you can sign in with Google, GitHub, or Apple SSO.
35
+
36
+ Session state lives at `~/.perplexity-mcp/` (cookies, profile, models cache). Delete that directory to start over, or use `npx perplexity-user-mcp logout --purge`.
37
+
38
+ Programmatic library use is supported but logging in still happens via the CLI command — the library exposes the runner spawn helpers but does not provide an interactive prompt.
39
+
40
+ ## Speed Boost (impit)
41
+
42
+ Speed Boost is an optional Rust-backed HTTP client (`impit`) that lets the server skip the browser for the tools that only need a cookie jar. Once installed it is auto-detected and used transparently — there is no env var to flip on.
43
+
44
+ Tools that benefit from Speed Boost:
45
+
46
+ - `perplexity_sync_cloud`
47
+ - `perplexity_hydrate_cloud_entry`
48
+ - `perplexity_retrieve`
49
+ - `perplexity_login` (auto-used when installed; opt out with `PERPLEXITY_DISABLE_IMPIT_LOGIN=1` or `--no-impit` on `login`)
50
+ - `perplexity_export`
51
+ - `perplexity_models`
52
+
53
+ Tools that still use the browser: `perplexity_search`, `perplexity_reason`, `perplexity_research`, `perplexity_compute`, `perplexity_ask`. (Search-via-impit is an opt-in pilot.)
54
+
55
+ Install:
56
+
57
+ ```bash
58
+ npx perplexity-user-mcp install-speed-boost
59
+ ```
60
+
61
+ Uninstall:
62
+
63
+ ```bash
64
+ npx perplexity-user-mcp uninstall-speed-boost
65
+ ```
66
+
67
+ There is no opt-out for the cloud-sync / hydrate / retrieve / export / models impit paths — every one of them falls back to the browser automatically on any impit failure (Cloudflare challenge, network error, parse error, etc.). The opt-out env vars only apply to login.
68
+
69
+ Speed Boost lives at `~/.perplexity-mcp/native-deps/node_modules/impit/`. You can remove it with the uninstall command above or with `rm -rf ~/.perplexity-mcp/native-deps/`.
70
+
71
+ ## Browser requirement
72
+
73
+ The server automates a real browser to reach Perplexity (Cloudflare-protected). Any of these work out of the box, probed in the order listed:
74
+
75
+ 1. **Google Chrome** *(recommended — best Cloudflare compatibility)*
76
+ 2. **Microsoft Edge** (all three platforms)
77
+ 3. **System Chromium** (mainly Linux)
78
+ 4. **Brave Browser** (Chromium-based — works unchanged)
79
+ 5. **Patchright's bundled Chromium**, downloaded with:
80
+
81
+ ```bash
82
+ npx patchright install chromium
83
+ ```
84
+
85
+ If none of those are installed the server exits at startup with instructions.
86
+
87
+ ### Picking a specific browser
88
+
89
+ All overrides are optional and evaluated at call time:
90
+
91
+ | Variable | Effect |
92
+ |---|---|
93
+ | `PERPLEXITY_BROWSER_PATH` | Absolute path to an executable. Takes precedence over auto-detection. |
94
+ | `PERPLEXITY_BROWSER_CHANNEL` | `chrome` \| `msedge` \| `chromium`. Controls which Patchright channel is used. |
95
+ | `PERPLEXITY_CHROME_PATH` | Legacy alias for `PERPLEXITY_BROWSER_PATH`. Still honored. |
96
+
97
+ ## CLI commands
98
+
99
+ Most-used commands. Run `npx perplexity-user-mcp --help` for the full list (daemon, tunnel providers, etc.).
100
+
101
+ ```bash
102
+ npx perplexity-user-mcp # start MCP stdio server
103
+ npx perplexity-user-mcp login [--profile X] [--mode auto|manual] [--plain-cookies]
104
+ npx perplexity-user-mcp logout [--profile X] [--purge]
105
+ npx perplexity-user-mcp status [--profile X] [--all]
106
+ npx perplexity-user-mcp doctor [--profile X] [--probe] [--all] [--report]
107
+ npx perplexity-user-mcp install-browser
108
+ npx perplexity-user-mcp install-speed-boost
109
+ npx perplexity-user-mcp uninstall-speed-boost
110
+ npx perplexity-user-mcp add-account [--name X] [--email Y] [--mode auto|manual] [--plain-cookies]
111
+ npx perplexity-user-mcp switch-account <name>
112
+ npx perplexity-user-mcp list-accounts
113
+ npx perplexity-user-mcp export <id> --format pdf|md|docx [--out path]
114
+ npx perplexity-user-mcp open <id> [--viewer obsidian|typora|logseq|system]
115
+ npx perplexity-user-mcp rebuild-history-index [--profile X]
116
+ npx perplexity-user-mcp sync-cloud [--profile X] [--page-size N] [--verbose]
117
+ npx perplexity-user-mcp daemon start [--port N] [--tunnel]
118
+ npx perplexity-user-mcp --version
119
+ ```
120
+
121
+ ## MCP client configuration
122
+
123
+ Example `mcp.json` entry (Cursor, Windsurf, Claude Code format):
124
+
125
+ ```json
126
+ {
127
+ "mcpServers": {
128
+ "perplexity": {
129
+ "command": "npx",
130
+ "args": ["-y", "perplexity-user-mcp"]
131
+ }
132
+ }
133
+ }
134
+ ```
135
+
136
+ Claude Desktop (`claude_desktop_config.json`) uses the same shape.
137
+
138
+ ## Environment variables
139
+
140
+ | Variable | Purpose |
141
+ |---|---|
142
+ | `PERPLEXITY_CONFIG_DIR` | Override `~/.perplexity-mcp` config/profile location. |
143
+ | `PERPLEXITY_BROWSER_PATH` | Explicit browser executable path (skips auto-detection). |
144
+ | `PERPLEXITY_BROWSER_CHANNEL` | `chrome` / `msedge` / `chromium`. Channel passed to Patchright. |
145
+ | `PERPLEXITY_CHROME_PATH` | **Legacy** alias for `PERPLEXITY_BROWSER_PATH`. Still honored. |
146
+ | `PERPLEXITY_HEADLESS_ONLY` | `1` to skip the headed Turnstile bootstrap and rely on cached `cf_clearance`. Useful on servers; fails if no clearance is cached yet. |
147
+ | `PERPLEXITY_SESSION_TOKEN` | Pre-supplied `__Secure-next-auth.session-token` (skips interactive login). |
148
+ | `PERPLEXITY_CSRF_TOKEN` | Optional companion to `PERPLEXITY_SESSION_TOKEN`. |
149
+ | `PERPLEXITY_DISABLE_IMPIT_LOGIN` | `1` to force browser-driven login even when Speed Boost is installed. |
150
+ | `PERPLEXITY_VAULT_PASSPHRASE` | Env-var master-key fallback for headless Linux (no keychain). |
151
+
152
+ ## Tools exposed over MCP
153
+
154
+ - `perplexity_search` — fast web search with citations
155
+ - `perplexity_reason` — step-by-step reasoning (Pro tier)
156
+ - `perplexity_research` — deep multi-section reports (Pro tier)
157
+ - `perplexity_ask` — flexible queries with explicit model/mode/follow-up control
158
+ - `perplexity_compute` — Computer mode / ASI (requires Computer-mode access — typically Max)
159
+ - `perplexity_models` — list models, account tier, rate limits
160
+ - `perplexity_retrieve` — poll a pending research/compute task
161
+ - `perplexity_export` — export a saved history entry as PDF, Markdown, or DOCX (uses Perplexity's native export endpoint with a local Markdown fallback)
162
+ - `perplexity_sync_cloud` — sync Perplexity cloud thread history into the local history store
163
+ - `perplexity_hydrate_cloud_entry` — hydrate a single cloud-backed history entry on demand
164
+ - `perplexity_list_researches` / `perplexity_get_research` — saved research history
165
+ - `perplexity_login` — returns login instructions (interactive login runs via the CLI / extension)
166
+ - `perplexity_doctor` — run diagnostic checks across browser, profile, auth, and network and return a Markdown report (pass `probe:true` for a live search probe)
167
+
168
+ ## Library use
169
+
170
+ Subpath exports are published for embedding the same runtime inside other Node tooling:
171
+
172
+ ```ts
173
+ import { PerplexityClient } from "perplexity-user-mcp/client";
174
+ import { CONFIG_DIR, BROWSER_DATA_DIR } from "perplexity-user-mcp/config";
175
+ import { readHistory } from "perplexity-user-mcp";
176
+ ```
177
+
178
+ Logging in still goes through the CLI (`npx perplexity-user-mcp login`) — the library does not expose an interactive prompt.
179
+
180
+ ## Requirements
181
+
182
+ - Node.js >= 20
183
+ - A browser runtime — any of: real Chrome, Microsoft Edge, Brave, system Chromium, or patchright's bundled Chromium (see the [Browser requirement](#browser-requirement) section)
184
+ - An active Perplexity account (free tier works; Pro/Max unlock reason/research/compute)
185
+
186
+ ## Issues
187
+
188
+ Bug reports and feature requests: <https://github.com/Automations-Project/VSCode-Perplexity-MCP/issues>.
189
+
190
+ ## License
191
+
192
+ MIT — see [LICENSE](../../LICENSE).
@@ -0,0 +1,20 @@
1
+ interface DownloadedAttachment {
2
+ filename: string;
3
+ path: string;
4
+ sizeBytes: number;
5
+ mimeType?: string;
6
+ kind: "image" | "file";
7
+ }
8
+
9
+ declare const MAX_ATTACHMENT_BYTES: number;
10
+ declare function sanitizeAttachmentFilename(name: string): string;
11
+ declare function inferAttachmentKind(mimeType?: string): "image" | "file";
12
+ declare function downloadAttachment(options: {
13
+ download: (url: string, targetPath: string) => Promise<unknown>;
14
+ url: string;
15
+ attachmentsDir: string;
16
+ filename?: string;
17
+ mimeType?: string;
18
+ }): Promise<DownloadedAttachment>;
19
+
20
+ export { type DownloadedAttachment, MAX_ATTACHMENT_BYTES, downloadAttachment, inferAttachmentKind, sanitizeAttachmentFilename };
@@ -0,0 +1,43 @@
1
+ import "./chunk-4UEJOM6W.mjs";
2
+
3
+ // src/attachments.js
4
+ import { mkdirSync, statSync } from "fs";
5
+ import { basename, join } from "path";
6
+ var MAX_ATTACHMENT_BYTES = 50 * 1024 * 1024;
7
+ function sanitizeAttachmentFilename(name) {
8
+ const cleaned = String(name ?? "").replace(/[<>:"/\\|?*\u0000-\u001F]/g, "-").replace(/\s+/g, " ").trim();
9
+ return cleaned || "attachment";
10
+ }
11
+ function inferAttachmentKind(mimeType) {
12
+ return String(mimeType ?? "").startsWith("image/") ? "image" : "file";
13
+ }
14
+ async function downloadAttachment(options) {
15
+ const {
16
+ download,
17
+ url,
18
+ attachmentsDir,
19
+ filename,
20
+ mimeType
21
+ } = options;
22
+ mkdirSync(attachmentsDir, { recursive: true });
23
+ const safeFilename = sanitizeAttachmentFilename(filename || basename(new URL(url).pathname));
24
+ const targetPath = join(attachmentsDir, safeFilename);
25
+ await download(url, targetPath);
26
+ const sizeBytes = statSync(targetPath).size;
27
+ if (sizeBytes > MAX_ATTACHMENT_BYTES) {
28
+ throw new Error(`Attachment '${safeFilename}' exceeds the 50 MB inline cap.`);
29
+ }
30
+ return {
31
+ filename: safeFilename,
32
+ path: targetPath,
33
+ sizeBytes,
34
+ mimeType: mimeType ?? void 0,
35
+ kind: inferAttachmentKind(mimeType)
36
+ };
37
+ }
38
+ export {
39
+ MAX_ATTACHMENT_BYTES,
40
+ downloadAttachment,
41
+ inferAttachmentKind,
42
+ sanitizeAttachmentFilename
43
+ };
@@ -0,0 +1,100 @@
1
+ import { spawn } from 'node:child_process';
2
+
3
+ const CATEGORY = "browser";
4
+
5
+ /**
6
+ * Read the Chrome-family browser's version string without launching the
7
+ * browser GUI.
8
+ *
9
+ * Why this is non-trivial: on Windows, `chrome.exe --version` invoked from a
10
+ * non-console parent (the VS Code extension host) forks the browser process —
11
+ * the original exits with code 0 and empty stdout (which is why the doctor
12
+ * report's `chrome-family` message was blank), and the forked children stay
13
+ * alive as visible Chrome windows. Every `runDoctor()` call (Run, Deep check,
14
+ * Capture diagnostics, Export) was therefore spawning a permanent visible
15
+ * window.
16
+ *
17
+ * Fix: on Windows, query the PE header's ProductVersion via PowerShell's
18
+ * `Get-Item ... .VersionInfo.ProductVersion` — no browser launch, returns
19
+ * the same string the user sees in File Properties → Details. On macOS /
20
+ * Linux, `--version` is a true CLI app contract and remains safe.
21
+ */
22
+ function probeVersion(path) {
23
+ if (process.platform === "win32") {
24
+ return new Promise((resolve, reject) => {
25
+ // Single-quote escape: PowerShell single-quoted strings need '' to
26
+ // represent a literal '. -LiteralPath bypasses wildcard expansion so
27
+ // bracketed install paths (e.g. Program Files (x86)) are safe.
28
+ const escaped = path.replace(/'/g, "''");
29
+ const child = spawn(
30
+ "powershell.exe",
31
+ [
32
+ "-NoProfile",
33
+ "-NonInteractive",
34
+ "-Command",
35
+ `(Get-Item -LiteralPath '${escaped}').VersionInfo.ProductVersion`,
36
+ ],
37
+ { timeout: 3000, windowsHide: true },
38
+ );
39
+ let out = "";
40
+ let err = "";
41
+ child.stdout?.on("data", (d) => { out += d.toString(); });
42
+ child.stderr?.on("data", (d) => { err += d.toString(); });
43
+ child.on("close", (code) => {
44
+ const trimmed = out.trim();
45
+ if (code === 0 && trimmed) resolve(trimmed);
46
+ else reject(new Error(err.trim() || `version probe exited ${code}`));
47
+ });
48
+ child.on("error", reject);
49
+ });
50
+ }
51
+ return new Promise((resolve, reject) => {
52
+ const child = spawn(path, ["--version"], { timeout: 2000 });
53
+ let out = "";
54
+ let err = "";
55
+ child.stdout?.on("data", (d) => { out += d.toString(); });
56
+ child.stderr?.on("data", (d) => { err += d.toString(); });
57
+ child.on("close", (code) => {
58
+ if (code === 0) resolve(out.trim() || err.trim());
59
+ else reject(new Error(err.trim() || `version probe exited ${code}`));
60
+ });
61
+ child.on("error", reject);
62
+ });
63
+ }
64
+
65
+ async function run(opts = {}) {
66
+ const results = [];
67
+ const findChrome = opts.findChromeOverride ?? (await import('../config.d.ts')).findChromeExecutable;
68
+ const versionProbe = opts.versionProbeOverride ?? probeVersion;
69
+
70
+ let chromePath = null;
71
+ try { chromePath = findChrome(); } catch { chromePath = null; }
72
+
73
+ if (!chromePath) {
74
+ results.push({
75
+ category: CATEGORY,
76
+ name: "chrome-family",
77
+ status: "fail",
78
+ message: "No Chrome / Edge / Chromium binary found on PATH or in standard locations.",
79
+ hint: "Run `npx perplexity-user-mcp install-browser` (Phase 4) or install Chrome/Edge manually.",
80
+ });
81
+ return results;
82
+ }
83
+
84
+ try {
85
+ const v = await versionProbe(chromePath);
86
+ results.push({ category: CATEGORY, name: "chrome-family", status: "pass", message: v });
87
+ } catch (err) {
88
+ results.push({ category: CATEGORY, name: "chrome-family", status: "pass", message: chromePath });
89
+ results.push({
90
+ category: CATEGORY,
91
+ name: "chrome-version",
92
+ status: "warn",
93
+ message: `version probe failed: ${err.message}`,
94
+ });
95
+ }
96
+
97
+ return results;
98
+ }
99
+
100
+ export { run };
@@ -0,0 +1,89 @@
1
+ import "../chunk-4UEJOM6W.mjs";
2
+
3
+ // src/checks/browser.js
4
+ import { spawn } from "child_process";
5
+ var CATEGORY = "browser";
6
+ function probeVersion(path) {
7
+ if (process.platform === "win32") {
8
+ return new Promise((resolve, reject) => {
9
+ const escaped = path.replace(/'/g, "''");
10
+ const child = spawn(
11
+ "powershell.exe",
12
+ [
13
+ "-NoProfile",
14
+ "-NonInteractive",
15
+ "-Command",
16
+ `(Get-Item -LiteralPath '${escaped}').VersionInfo.ProductVersion`
17
+ ],
18
+ { timeout: 3e3, windowsHide: true }
19
+ );
20
+ let out = "";
21
+ let err = "";
22
+ child.stdout?.on("data", (d) => {
23
+ out += d.toString();
24
+ });
25
+ child.stderr?.on("data", (d) => {
26
+ err += d.toString();
27
+ });
28
+ child.on("close", (code) => {
29
+ const trimmed = out.trim();
30
+ if (code === 0 && trimmed) resolve(trimmed);
31
+ else reject(new Error(err.trim() || `version probe exited ${code}`));
32
+ });
33
+ child.on("error", reject);
34
+ });
35
+ }
36
+ return new Promise((resolve, reject) => {
37
+ const child = spawn(path, ["--version"], { timeout: 2e3 });
38
+ let out = "";
39
+ let err = "";
40
+ child.stdout?.on("data", (d) => {
41
+ out += d.toString();
42
+ });
43
+ child.stderr?.on("data", (d) => {
44
+ err += d.toString();
45
+ });
46
+ child.on("close", (code) => {
47
+ if (code === 0) resolve(out.trim() || err.trim());
48
+ else reject(new Error(err.trim() || `version probe exited ${code}`));
49
+ });
50
+ child.on("error", reject);
51
+ });
52
+ }
53
+ async function run(opts = {}) {
54
+ const results = [];
55
+ const findChrome = opts.findChromeOverride ?? (await import("../config.mjs")).findChromeExecutable;
56
+ const versionProbe = opts.versionProbeOverride ?? probeVersion;
57
+ let chromePath = null;
58
+ try {
59
+ chromePath = findChrome();
60
+ } catch {
61
+ chromePath = null;
62
+ }
63
+ if (!chromePath) {
64
+ results.push({
65
+ category: CATEGORY,
66
+ name: "chrome-family",
67
+ status: "fail",
68
+ message: "No Chrome / Edge / Chromium binary found on PATH or in standard locations.",
69
+ hint: "Run `npx perplexity-user-mcp install-browser` (Phase 4) or install Chrome/Edge manually."
70
+ });
71
+ return results;
72
+ }
73
+ try {
74
+ const v = await versionProbe(chromePath);
75
+ results.push({ category: CATEGORY, name: "chrome-family", status: "pass", message: v });
76
+ } catch (err) {
77
+ results.push({ category: CATEGORY, name: "chrome-family", status: "pass", message: chromePath });
78
+ results.push({
79
+ category: CATEGORY,
80
+ name: "chrome-version",
81
+ status: "warn",
82
+ message: `version probe failed: ${err.message}`
83
+ });
84
+ }
85
+ return results;
86
+ }
87
+ export {
88
+ run
89
+ };
@@ -0,0 +1,91 @@
1
+ import { existsSync, statSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ const CATEGORY = "config";
5
+
6
+ async function run(opts = {}) {
7
+ const dir = opts.configDir;
8
+ const results = [];
9
+
10
+ if (!existsSync(dir)) {
11
+ results.push({
12
+ category: CATEGORY,
13
+ name: "config-dir",
14
+ status: "fail",
15
+ message: `Config dir not found: ${dir}`,
16
+ hint: "Run `npx perplexity-user-mcp login` to initialize it.",
17
+ });
18
+ return results;
19
+ }
20
+ results.push({
21
+ category: CATEGORY,
22
+ name: "config-dir",
23
+ status: "pass",
24
+ message: `Config dir present at ${dir}`,
25
+ });
26
+
27
+ if (process.platform !== "win32") {
28
+ const mode = statSync(dir).mode & 0o777;
29
+ if (mode & 0o077) {
30
+ results.push({
31
+ category: CATEGORY,
32
+ name: "config-perms",
33
+ status: "warn",
34
+ message: `Config dir is world/group readable (mode 0${mode.toString(8)})`,
35
+ hint: "Run `chmod 700 ~/.perplexity-mcp`.",
36
+ });
37
+ } else {
38
+ results.push({ category: CATEGORY, name: "config-perms", status: "pass", message: "0700" });
39
+ }
40
+ } else {
41
+ results.push({ category: CATEGORY, name: "config-perms", status: "skip", message: "NTFS ACL (see icacls)" });
42
+ }
43
+
44
+ const activePath = join(dir, "active");
45
+ if (!existsSync(activePath)) {
46
+ results.push({
47
+ category: CATEGORY,
48
+ name: "active-pointer",
49
+ status: "warn",
50
+ message: "No active profile set.",
51
+ hint: "Add an account to create and activate your first profile.",
52
+ action: { label: "Add account", commandId: "Perplexity.addAccount" },
53
+ });
54
+ } else {
55
+ const name = readFileSync(activePath, "utf8").trim();
56
+ const metaPath = join(dir, "profiles", name, "meta.json");
57
+ if (!existsSync(metaPath)) {
58
+ results.push({
59
+ category: CATEGORY,
60
+ name: "active-pointer",
61
+ status: "fail",
62
+ message: `active -> '${name}' but profile does not exist`,
63
+ hint: "Run `npx perplexity-user-mcp list-accounts` and pick a real profile.",
64
+ });
65
+ } else {
66
+ results.push({ category: CATEGORY, name: "active-pointer", status: "pass", message: name });
67
+ }
68
+ }
69
+
70
+ const cfgJson = join(dir, "config.json");
71
+ if (!existsSync(cfgJson)) {
72
+ results.push({ category: CATEGORY, name: "config-json", status: "skip", message: "optional file absent" });
73
+ } else {
74
+ try {
75
+ JSON.parse(readFileSync(cfgJson, "utf8"));
76
+ results.push({ category: CATEGORY, name: "config-json", status: "pass", message: "valid" });
77
+ } catch (err) {
78
+ results.push({
79
+ category: CATEGORY,
80
+ name: "config-json",
81
+ status: "warn",
82
+ message: `config.json malformed: ${err.message}`,
83
+ hint: "Delete or fix the file — doctor reads it for reporting.githubIssueButton etc.",
84
+ });
85
+ }
86
+ }
87
+
88
+ return results;
89
+ }
90
+
91
+ export { run };
@@ -0,0 +1,88 @@
1
+ import "../chunk-4UEJOM6W.mjs";
2
+
3
+ // src/checks/config.js
4
+ import { existsSync, readFileSync, statSync } from "fs";
5
+ import { join } from "path";
6
+ var CATEGORY = "config";
7
+ async function run(opts = {}) {
8
+ const dir = opts.configDir;
9
+ const results = [];
10
+ if (!existsSync(dir)) {
11
+ results.push({
12
+ category: CATEGORY,
13
+ name: "config-dir",
14
+ status: "fail",
15
+ message: `Config dir not found: ${dir}`,
16
+ hint: "Run `npx perplexity-user-mcp login` to initialize it."
17
+ });
18
+ return results;
19
+ }
20
+ results.push({
21
+ category: CATEGORY,
22
+ name: "config-dir",
23
+ status: "pass",
24
+ message: `Config dir present at ${dir}`
25
+ });
26
+ if (process.platform !== "win32") {
27
+ const mode = statSync(dir).mode & 511;
28
+ if (mode & 63) {
29
+ results.push({
30
+ category: CATEGORY,
31
+ name: "config-perms",
32
+ status: "warn",
33
+ message: `Config dir is world/group readable (mode 0${mode.toString(8)})`,
34
+ hint: "Run `chmod 700 ~/.perplexity-mcp`."
35
+ });
36
+ } else {
37
+ results.push({ category: CATEGORY, name: "config-perms", status: "pass", message: "0700" });
38
+ }
39
+ } else {
40
+ results.push({ category: CATEGORY, name: "config-perms", status: "skip", message: "NTFS ACL (see icacls)" });
41
+ }
42
+ const activePath = join(dir, "active");
43
+ if (!existsSync(activePath)) {
44
+ results.push({
45
+ category: CATEGORY,
46
+ name: "active-pointer",
47
+ status: "warn",
48
+ message: "No active profile set.",
49
+ hint: "Add an account to create and activate your first profile.",
50
+ action: { label: "Add account", commandId: "Perplexity.addAccount" }
51
+ });
52
+ } else {
53
+ const name = readFileSync(activePath, "utf8").trim();
54
+ const metaPath = join(dir, "profiles", name, "meta.json");
55
+ if (!existsSync(metaPath)) {
56
+ results.push({
57
+ category: CATEGORY,
58
+ name: "active-pointer",
59
+ status: "fail",
60
+ message: `active -> '${name}' but profile does not exist`,
61
+ hint: "Run `npx perplexity-user-mcp list-accounts` and pick a real profile."
62
+ });
63
+ } else {
64
+ results.push({ category: CATEGORY, name: "active-pointer", status: "pass", message: name });
65
+ }
66
+ }
67
+ const cfgJson = join(dir, "config.json");
68
+ if (!existsSync(cfgJson)) {
69
+ results.push({ category: CATEGORY, name: "config-json", status: "skip", message: "optional file absent" });
70
+ } else {
71
+ try {
72
+ JSON.parse(readFileSync(cfgJson, "utf8"));
73
+ results.push({ category: CATEGORY, name: "config-json", status: "pass", message: "valid" });
74
+ } catch (err) {
75
+ results.push({
76
+ category: CATEGORY,
77
+ name: "config-json",
78
+ status: "warn",
79
+ message: `config.json malformed: ${err.message}`,
80
+ hint: "Delete or fix the file \u2014 doctor reads it for reporting.githubIssueButton etc."
81
+ });
82
+ }
83
+ }
84
+ return results;
85
+ }
86
+ export {
87
+ run
88
+ };