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
@@ -1,89 +1,24 @@
1
- import { readFileSync, existsSync } from 'node:fs';
2
- import { fileURLToPath } from 'node:url';
3
- import { spawn } from 'node:child_process';
4
- import { join } from 'node:path';
5
-
6
- const CATEGORY = "runtime";
7
-
8
- function parseMajor(v) {
9
- const m = /^v?(\d+)\./.exec(v);
10
- return m ? Number(m[1]) : NaN;
11
- }
12
-
13
- function defaultGitShaResolver(cwd) {
14
- return new Promise((resolve) => {
15
- const child = spawn("git", ["rev-parse", "--short", "HEAD"], { cwd, timeout: 2000 });
16
- let out = "";
17
- child.stdout?.on("data", (d) => { out += d.toString(); });
18
- child.on("close", (code) => resolve(code === 0 ? out.trim() : null));
19
- child.on("error", () => resolve(null));
20
- });
21
- }
22
-
23
- function getPackageJsonCandidates(baseDir) {
24
- const candidates = [];
25
- if (baseDir) {
26
- candidates.push(join(baseDir, "mcp", "package.json"));
27
- candidates.push(join(baseDir, "package.json"));
28
- }
29
- const metaUrl = import.meta.url ?? null;
30
- if (metaUrl) {
31
- try {
32
- candidates.push(fileURLToPath(new URL("../../package.json", metaUrl)));
33
- } catch {}
34
- try {
35
- candidates.push(fileURLToPath(new URL("../package.json", metaUrl)));
36
- } catch {}
37
- }
38
- return candidates;
39
- }
40
-
41
- async function run(opts = {}) {
42
- const results = [];
43
- const nodeVersion = opts.nodeVersionOverride ?? process.version;
44
- const major = parseMajor(nodeVersion);
45
- results.push({
46
- category: CATEGORY,
47
- name: "node-version",
48
- status: major >= 20 ? "pass" : "fail",
49
- message: `Node.js ${nodeVersion}`,
50
- hint: major >= 20 ? undefined : "Upgrade to Node 20 or later (https://nodejs.org).",
51
- });
52
- results.push({ category: CATEGORY, name: "platform", status: "pass", message: `${process.platform} ${process.arch}` });
53
- results.push({ category: CATEGORY, name: "arch", status: "pass", message: process.arch });
54
-
55
- let version = "0.0.0";
56
- for (const pkgPath of getPackageJsonCandidates(opts.baseDir)) {
57
- try {
58
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
59
- if (pkg.name === "perplexity-user-mcp" && pkg.version) {
60
- version = pkg.version;
61
- break;
62
- }
63
- } catch {}
64
- }
65
- results.push({
66
- category: CATEGORY,
67
- name: "package-version",
68
- status: "pass",
69
- message: `perplexity-user-mcp ${version}`,
70
- detail: { version },
71
- });
72
-
73
- const gitDir = opts.gitDirOverride ?? join(process.cwd(), ".git");
74
- const resolver = opts.gitShaResolverOverride ?? defaultGitShaResolver;
75
- if (!existsSync(gitDir)) {
76
- results.push({ category: CATEGORY, name: "git-sha", status: "skip", message: "not a git checkout" });
77
- } else {
78
- const sha = await resolver(process.cwd());
79
- if (sha) {
80
- results.push({ category: CATEGORY, name: "git-sha", status: "pass", message: sha });
81
- } else {
82
- results.push({ category: CATEGORY, name: "git-sha", status: "skip", message: "git not on PATH" });
83
- }
84
- }
85
-
86
- return results;
87
- }
88
-
89
- export { run };
1
+ export function run(opts?: {}): Promise<({
2
+ category: string;
3
+ name: string;
4
+ status: string;
5
+ message: string;
6
+ hint: string | undefined;
7
+ detail?: undefined;
8
+ } | {
9
+ category: string;
10
+ name: string;
11
+ status: string;
12
+ message: string;
13
+ detail: {
14
+ version: string;
15
+ };
16
+ hint?: undefined;
17
+ } | {
18
+ category: string;
19
+ name: string;
20
+ status: string;
21
+ message: any;
22
+ hint?: undefined;
23
+ detail?: undefined;
24
+ })[]>;
@@ -1,142 +1,13 @@
1
- import { existsSync } from 'node:fs';
2
- import { join } from 'node:path';
3
-
4
- const CATEGORY = "vault";
5
-
6
- async function tryKeychain() {
7
- try {
8
- const mod = await import('keytar');
9
- const keytar = mod.default ?? mod;
10
- const hex = await keytar.getPassword("perplexity-user-mcp", "vault-master-key");
11
- return { available: true, hasKey: !!hex };
12
- } catch {
13
- return { available: false, hasKey: false };
14
- }
15
- }
16
-
17
- function keychainExpected() {
18
- return process.platform === "win32" || process.platform === "darwin" ||
19
- (process.platform === "linux" && !process.env.CI);
20
- }
21
-
22
- async function run(opts = {}) {
23
- const results = [];
24
- const dir = opts.configDir;
25
- const profile = opts.profile ?? "default";
26
- const enc = join(dir, "profiles", profile, "vault.enc");
27
- const plain = join(dir, "profiles", profile, "vault.json");
28
- const envPass = process.env.PERPLEXITY_VAULT_PASSPHRASE;
29
- const kc = await tryKeychain();
30
-
31
- // Encryption mode (separate from unseal path so plaintext opt-out is always a warn, not a skip).
32
- if (existsSync(plain)) {
33
- results.push({
34
- category: CATEGORY,
35
- name: "encryption",
36
- status: "warn",
37
- message: "plaintext vault.json (security.encryptCookies=false)",
38
- hint: "Re-run login without --plain-cookies to enable AES-256-GCM at rest.",
39
- });
40
- } else if (existsSync(enc)) {
41
- results.push({ category: CATEGORY, name: "encryption", status: "pass", message: "AES-256-GCM (vault.enc)" });
42
- } else {
43
- results.push({ category: CATEGORY, name: "encryption", status: "skip", message: "no vault yet" });
44
- }
45
-
46
- // Unseal path resolution, matching vault.js getMasterKey() priority.
47
- if (kc.hasKey) {
48
- results.push({ category: CATEGORY, name: "unseal-path", status: "pass", message: "OS keychain holds master key" });
49
- if (envPass) {
50
- results.push({
51
- category: CATEGORY,
52
- name: "keychain-preferred",
53
- status: "warn",
54
- message: "PERPLEXITY_VAULT_PASSPHRASE is also set — keychain wins, but consider removing the env var",
55
- });
56
- }
57
- } else if (envPass) {
58
- const hasKc = kc.available;
59
- results.push({
60
- category: CATEGORY,
61
- name: "unseal-path",
62
- status: "pass",
63
- message: `env var ${hasKc ? "(keychain available but empty)" : "(keychain unavailable — expected on headless Linux)"}`,
64
- });
65
- if (hasKc) {
66
- results.push({
67
- category: CATEGORY,
68
- name: "keychain-preferred",
69
- status: "warn",
70
- message: "keychain is available — moving the master key there would remove the passphrase from IDE config files",
71
- hint: "Run `npx perplexity-user-mcp login` once with the env var unset; the key will be written to keychain.",
72
- });
73
- } else {
74
- results.push({ category: CATEGORY, name: "keychain-preferred", status: "skip", message: "keychain not applicable" });
75
- }
76
- } else {
77
- // No keychain, no env var.
78
- if (!existsSync(enc) && !existsSync(plain)) {
79
- results.push({ category: CATEGORY, name: "unseal-path", status: "skip", message: "no vault to unseal yet" });
80
- } else if (existsSync(plain)) {
81
- results.push({ category: CATEGORY, name: "unseal-path", status: "pass", message: "plaintext — no key required" });
82
- } else {
83
- const ttyLikely = process.stdin?.isTTY === true;
84
- results.push({
85
- category: CATEGORY,
86
- name: "unseal-path",
87
- status: ttyLikely ? "warn" : "fail",
88
- message: ttyLikely
89
- ? "no keychain, no env var — TTY prompt will be required on next use"
90
- : "vault locked: no keychain, no env var, no TTY",
91
- hint: keychainExpected()
92
- ? "Install libsecret+gnome-keyring (Linux), or set PERPLEXITY_VAULT_PASSPHRASE."
93
- : "Set PERPLEXITY_VAULT_PASSPHRASE in your MCP config env.",
94
- });
95
- }
96
- }
97
-
98
- // Active-decrypt verification — only when an encrypted vault.enc actually
99
- // exists. This catches the "user has both keychain + passphrase set, but
100
- // vault.enc was written with one and the read path now prefers the other"
101
- // failure mode that surfaces as "Vault decrypt failed: wrong passphrase
102
- // or corrupted ciphertext" mid-login. A status check that just reports
103
- // "OS keychain holds master key" is misleading if the key can't actually
104
- // open the on-disk blob.
105
- if (existsSync(enc) && (kc.hasKey || envPass)) {
106
- try {
107
- const { Vault, __resetKeyCache } = await import('../vault.d-BSJWDLhp.d.ts');
108
- // Use a fresh resolution context so the doctor's verification doesn't
109
- // pollute the cached unseal material for the rest of the process.
110
- __resetKeyCache();
111
- // Vault.get returns null for absent keys without throwing; only a
112
- // genuine decrypt failure throws.
113
- await new Vault().get(profile, "cookies");
114
- results.push({
115
- category: CATEGORY,
116
- name: "unseal-verify",
117
- status: "pass",
118
- message: "vault.enc decrypts cleanly with the active unseal material",
119
- });
120
- } catch (err) {
121
- const msg = err instanceof Error ? err.message : String(err);
122
- const isDecryptFailure = /wrong passphrase or corrupted ciphertext|Vault decrypt failed/.test(msg);
123
- results.push({
124
- category: CATEGORY,
125
- name: "unseal-verify",
126
- status: "fail",
127
- message: isDecryptFailure
128
- ? "vault.enc cannot be decrypted with any available unseal material"
129
- : `vault.enc unreadable: ${msg}`,
130
- hint: isDecryptFailure
131
- ? (kc.hasKey && envPass
132
- ? "Both keychain and PERPLEXITY_VAULT_PASSPHRASE are set, but neither matches the blob. The blob was likely written under a since-rotated passphrase or a different keychain key. Run 'perplexity-user-mcp logout --purge' on this profile and log in again to write a fresh vault."
133
- : "The unseal material has changed since this blob was written. Restore the original passphrase, or run 'perplexity-user-mcp logout --purge' on this profile and log in again.")
134
- : "Inspect the file at the path under 'profiles' check; consider restoring from backup or purging.",
135
- });
136
- }
137
- }
138
-
139
- return results;
140
- }
141
-
142
- export { run };
1
+ export function run(opts?: {}): Promise<({
2
+ category: string;
3
+ name: string;
4
+ status: string;
5
+ message: string;
6
+ hint: string;
7
+ } | {
8
+ category: string;
9
+ name: string;
10
+ status: string;
11
+ message: string;
12
+ hint?: undefined;
13
+ })[]>;
@@ -1,19 +1,14 @@
1
+ import {
2
+ probeKeychainState
3
+ } from "../chunk-C5I7KXHK.mjs";
4
+ import "../chunk-MTDFKNXX.mjs";
5
+ import "../chunk-E3GRJXXJ.mjs";
1
6
  import "../chunk-4UEJOM6W.mjs";
2
7
 
3
8
  // src/checks/vault.js
4
9
  import { existsSync } from "fs";
5
10
  import { join } from "path";
6
11
  var CATEGORY = "vault";
7
- async function tryKeychain() {
8
- try {
9
- const mod = await import("keytar");
10
- const keytar = mod.default ?? mod;
11
- const hex = await keytar.getPassword("perplexity-user-mcp", "vault-master-key");
12
- return { available: true, hasKey: !!hex };
13
- } catch {
14
- return { available: false, hasKey: false };
15
- }
16
- }
17
12
  function keychainExpected() {
18
13
  return process.platform === "win32" || process.platform === "darwin" || process.platform === "linux" && !process.env.CI;
19
14
  }
@@ -24,7 +19,7 @@ async function run(opts = {}) {
24
19
  const enc = join(dir, "profiles", profile, "vault.enc");
25
20
  const plain = join(dir, "profiles", profile, "vault.json");
26
21
  const envPass = process.env.PERPLEXITY_VAULT_PASSPHRASE;
27
- const kc = await tryKeychain();
22
+ const kc = await probeKeychainState();
28
23
  if (existsSync(plain)) {
29
24
  results.push({
30
25
  category: CATEGORY,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getConfigDir,
3
3
  getProfilePaths
4
- } from "./chunk-HJIXH6CL.mjs";
4
+ } from "./chunk-E3GRJXXJ.mjs";
5
5
 
6
6
  // src/reinit-watcher.js
7
7
  import { existsSync, mkdirSync, watch } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getConfigDir
3
- } from "./chunk-HJIXH6CL.mjs";
3
+ } from "./chunk-E3GRJXXJ.mjs";
4
4
  import {
5
5
  __glob
6
6
  } from "./chunk-4UEJOM6W.mjs";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getActiveName,
3
3
  getProfilePaths
4
- } from "./chunk-HJIXH6CL.mjs";
4
+ } from "./chunk-E3GRJXXJ.mjs";
5
5
 
6
6
  // src/history-store.js
7
7
  import { randomUUID } from "crypto";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-MTDFKNXX.mjs";
4
4
  import {
5
5
  getProfilePaths
6
- } from "./chunk-HJIXH6CL.mjs";
6
+ } from "./chunk-E3GRJXXJ.mjs";
7
7
 
8
8
  // src/vault.js
9
9
  import { createCipheriv, createDecipheriv, randomBytes, hkdfSync, scrypt as nodeScrypt } from "crypto";
@@ -32,6 +32,7 @@ var V3_HEADER_FIXED_PREAMBLE = 4 + 1 + 1 + 1;
32
32
  var scryptAsync = promisify(nodeScrypt);
33
33
  var _kdfParamsOverride = null;
34
34
  var _kdfTestModeActive = false;
35
+ var _keytarModuleCache = null;
35
36
  function __setKdfParamsForTest(params) {
36
37
  if (!params || typeof params.logN !== "number" || typeof params.r !== "number" || typeof params.p !== "number") {
37
38
  throw new Error("__setKdfParamsForTest requires {logN, r, p} numbers.");
@@ -197,17 +198,30 @@ var KEYTAR_SERVICE = "perplexity-user-mcp";
197
198
  var KEYTAR_ACCOUNT = "vault-master-key";
198
199
  var _keyCache = null;
199
200
  var _unsealMaterialCache = null;
201
+ function isKeychainDisabled() {
202
+ return process.env.PERPLEXITY_DISABLE_KEYCHAIN === "1";
203
+ }
200
204
  function __resetKeyCache() {
201
205
  _keyCache = null;
202
206
  _unsealMaterialCache = null;
203
207
  _kdfParamsOverride = null;
204
208
  _kdfTestModeActive = false;
209
+ _keytarModuleCache = null;
205
210
  }
206
211
  async function tryKeytar() {
212
+ if (isKeychainDisabled()) return null;
213
+ if (_keytarModuleCache !== null) return _keytarModuleCache;
207
214
  try {
208
215
  const mod = await import("keytar");
209
- return mod.default ?? mod;
216
+ const keytar = mod.default ?? mod;
217
+ if (!keytar || typeof keytar.getPassword !== "function") {
218
+ _keytarModuleCache = false;
219
+ return null;
220
+ }
221
+ _keytarModuleCache = keytar;
222
+ return keytar;
210
223
  } catch {
224
+ _keytarModuleCache = false;
211
225
  return null;
212
226
  }
213
227
  }
@@ -228,6 +242,21 @@ async function keyFromKeychain() {
228
242
  return null;
229
243
  }
230
244
  }
245
+ async function probeKeychainState() {
246
+ if (isKeychainDisabled()) {
247
+ return { available: false, hasKey: false };
248
+ }
249
+ const keytar = await tryKeytar();
250
+ if (!keytar || typeof keytar.getPassword !== "function") {
251
+ return { available: false, hasKey: false };
252
+ }
253
+ try {
254
+ const hex = await keytar.getPassword(KEYTAR_SERVICE, KEYTAR_ACCOUNT);
255
+ return { available: true, hasKey: !!hex };
256
+ } catch {
257
+ return { available: true, hasKey: false };
258
+ }
259
+ }
231
260
  function isStdioServerMode() {
232
261
  return process.env.PERPLEXITY_MCP_STDIO === "1" || process.stdin && process.stdin.isTTY === false;
233
262
  }
@@ -383,6 +412,7 @@ export {
383
412
  encryptBlob,
384
413
  decryptBlob,
385
414
  __resetKeyCache,
415
+ probeKeychainState,
386
416
  getUnsealMaterial,
387
417
  getAllUnsealMaterials,
388
418
  getMasterKey,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getTunnelProvider,
3
3
  readTunnelSettings
4
- } from "./chunk-XTRJSV72.mjs";
4
+ } from "./chunk-LGH5BSUY.mjs";
5
5
  import {
6
6
  acquire,
7
7
  getLockfilePath,
@@ -9,27 +9,27 @@ import {
9
9
  read,
10
10
  release,
11
11
  replace
12
- } from "./chunk-KJFX2ZXR.mjs";
12
+ } from "./chunk-NMKNEEZB.mjs";
13
13
  import {
14
14
  getPackageVersion,
15
15
  startDaemonServer
16
- } from "./chunk-T6ARJK2P.mjs";
16
+ } from "./chunk-P6YOLJ5T.mjs";
17
17
  import {
18
18
  ensureToken,
19
19
  getTokenPath,
20
20
  readToken
21
- } from "./chunk-TDXETAQT.mjs";
21
+ } from "./chunk-DKEJZ4FI.mjs";
22
22
  import {
23
23
  watchActiveProfile,
24
24
  watchReinit
25
- } from "./chunk-WDIW33DA.mjs";
25
+ } from "./chunk-3LUO5ATM.mjs";
26
26
  import {
27
27
  PerplexityClient
28
- } from "./chunk-C3HPFFTD.mjs";
28
+ } from "./chunk-GBHPJ7I7.mjs";
29
29
  import {
30
30
  getActiveName,
31
31
  getConfigDir
32
- } from "./chunk-HJIXH6CL.mjs";
32
+ } from "./chunk-E3GRJXXJ.mjs";
33
33
 
34
34
  // src/daemon/launcher.ts
35
35
  import { spawn } from "child_process";
@@ -671,11 +671,14 @@ async function spawnDetachedDaemon(options) {
671
671
  if (options.tunnel) {
672
672
  args.push("--tunnel");
673
673
  }
674
+ const env = { ...process.env };
675
+ delete env.PERPLEXITY_HEADLESS_ONLY;
676
+ delete env.PERPLEXITY_NO_DAEMON;
674
677
  const child = spawn(process.execPath, args, {
675
678
  detached: true,
676
679
  stdio: "ignore",
677
680
  env: {
678
- ...process.env,
681
+ ...env,
679
682
  PERPLEXITY_CONFIG_DIR: options.configDir
680
683
  }
681
684
  });
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-MTDFKNXX.mjs";
4
4
  import {
5
5
  getConfigDir
6
- } from "./chunk-HJIXH6CL.mjs";
6
+ } from "./chunk-E3GRJXXJ.mjs";
7
7
 
8
8
  // src/daemon/token.ts
9
9
  import { randomBytes } from "crypto";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  Vault
3
- } from "./chunk-S677V2JU.mjs";
3
+ } from "./chunk-C5I7KXHK.mjs";
4
4
  import {
5
5
  getActiveName,
6
6
  getProfilePaths
7
- } from "./chunk-HJIXH6CL.mjs";
7
+ } from "./chunk-E3GRJXXJ.mjs";
8
8
 
9
9
  // src/config.ts
10
10
  import { existsSync } from "fs";
@@ -192,16 +192,35 @@ async function getSavedCookies() {
192
192
  }
193
193
  return cookies;
194
194
  }
195
- const raw = await _vault.get(activeName(), "cookies").catch((err) => {
195
+ const profile = activeName();
196
+ let unsealFailed = false;
197
+ const raw = await _vault.get(profile, "cookies").catch((err) => {
198
+ unsealFailed = true;
196
199
  const msg = err instanceof Error ? err.message : String(err);
197
- console.error(`[vault] getSavedCookies failed for profile ${activeName()}: ${msg}`);
200
+ console.error(`[vault] getSavedCookies failed for profile '${profile}': ${msg}`);
198
201
  return null;
199
202
  });
200
- if (!raw) return [];
203
+ if (!raw) {
204
+ if (!unsealFailed) {
205
+ const paths = getProfilePaths(profile);
206
+ if (!existsSync(paths.vault)) {
207
+ console.error(`[vault] getSavedCookies: no vault.enc for profile '${profile}' \u2014 run login first`);
208
+ } else {
209
+ console.error(`[vault] getSavedCookies: vault.enc exists for profile '${profile}' but 'cookies' key is absent`);
210
+ }
211
+ }
212
+ return [];
213
+ }
201
214
  try {
202
215
  const parsed = JSON.parse(raw);
203
- return Array.isArray(parsed) ? parsed : [];
204
- } catch {
216
+ if (!Array.isArray(parsed)) {
217
+ console.error(`[vault] getSavedCookies: 'cookies' value for profile '${profile}' is not an array (${typeof parsed})`);
218
+ return [];
219
+ }
220
+ return parsed;
221
+ } catch (err) {
222
+ const msg = err instanceof Error ? err.message : String(err);
223
+ console.error(`[vault] getSavedCookies: JSON parse failed for profile '${profile}': ${msg}`);
205
224
  return [];
206
225
  }
207
226
  }
@@ -17,7 +17,9 @@ function getProfilePaths(name) {
17
17
  vault: join(dir, "vault.enc"),
18
18
  vaultPlain: join(dir, "vault.json"),
19
19
  browserData: join(dir, "browser-data"),
20
+ loginBrowserData: join(dir, "login-browser-data"),
20
21
  modelsCache: join(dir, "models-cache.json"),
22
+ daemonStatus: join(dir, "daemon-status.json"),
21
23
  history: join(dir, "history"),
22
24
  attachments: join(dir, "attachments"),
23
25
  researches: join(dir, "researches"),