Skip to content

You've got a few bugs #6

Closed
Closed

Description

@juangrukat

Product

VS Code extension (perplexity-vscode)

Version

1

MCP Client (if applicable)

None

Perplexity tier

None

OS

Mac

Browser used by the MCP server

No response

What happened?

The upstream/general bugs we found, separate from the repo-trimming work, are:

  1. CLI silently exits when launched through a symlink
    The direct-run guard used:

    import.meta.url === pathToFileURL(process.argv[1]).href

    That fails when process.argv[1] is a symlink, because import.meta.url resolves to the real built file. Result: routeCommand() never runs and commands exit 0 with no output.

    Upstream fix: compare realpathSync(fileURLToPath(import.meta.url)) to realpathSync(process.argv[1]).

  2. Built dist/cli.mjs needs a real executable shebang
    The npm bin points at ./dist/cli.mjs. Without:

    #!/usr/bin/env node

    and executable mode, global installs or raw symlinked bins can fail or behave weirdly on macOS/Homebrew setups.

    Upstream fix: keep shebang in source or add it as part of build, and chmod the built CLI.

  3. Repeated macOS Keychain prompts
    The doctor/status/login paths can touch vault/keychain repeatedly, causing the user to be prompted many times. That is a real UX bug, not trim-specific.

    Upstream fix: cache keychain probe results per process, avoid repeated keytar.getPassword() calls in diagnostics, and add a supported escape hatch like PERPLEXITY_DISABLE_KEYCHAIN=1 for tests/headless diagnostics.

  4. Manual login browser session was not persistent enough
    The login flow opened Chrome but did not preserve Google SSO state reliably between attempts. If the Perplexity login depends on Google, users can get confused because the launched Chrome is not their normal Chrome profile.

    Upstream fix: use a dedicated persistent browser profile for MCP login, document where it lives, and clear stale Chrome singleton lock files before launch.

  5. Image payload rejection recovery in Hermes, adjacent but not this repo
    This one is not a Perplexity MCP bug directly, but it affected debugging: Hermes sent image_url blocks to a text-only DeepSeek endpoint and did not recognize the provider’s error wording.

Doctor output (optional, recommended)

Relevant logs or errors

Activity

  1. self-assigned this
    on May 11, 2026
  2. ARHAEEM commented on May 11, 2026

    @ARHAEEM
    Member

    Thanks for the careful writeup — confirmed all four upstream bugs against main. Bundling these into the 0.8.43 release that's already in flight for #5 since they touch overlapping CLI/login code paths and one VSIX smoke pass covers everything.

    Confirmed and planned

    1. CLI symlink exit — confirmed

    Two call sites bite, not one: packages/mcp-server/src/cli.js:1306 AND packages/mcp-server/src/index.ts:194. The index.ts one is the daemon entrypoint, so the same regression silently no-ops the daemon when the bin is symlinked too — worth fixing both.

    Fix shape:

    import { realpathSync } from "node:fs";
    import { fileURLToPath, pathToFileURL } from "node:url";
    
    function isMainModule() {
      if (!process.argv[1]) return false;
      try {
        const moduleReal = realpathSync(fileURLToPath(import.meta.url));
        const argvReal = realpathSync(process.argv[1]);
        return moduleReal === argvReal;
      } catch {
        return import.meta.url === pathToFileURL(process.argv[1]).href;
      }
    }

    Keep the original comparison as the catch fallback so we don't regress on platforms where realpath rejects (rare, but defense in depth). Drop the /* v8 ignore start */ block too — with realpath we can write a proper fixture.

    2. Built dist/cli.mjs shebang + exec mode — confirmed

    tsup.config.ts:82-87 explicitly does NOT emit a shebang and the comment claims "npm's bin linker wraps the entry with its own node invocation on install." That's wrong on POSIX — npm just creates a symlink at node_modules/.bin/perplexity-user-mcp → ../perplexity-user-mcp/dist/cli.mjs and chmods it 755. Kernel can't exec a file without a shebang or magic bytes; you get the silent-no-output failure mode you described.

    Fix shape:

    • Use tsup's banner: { js: "#!/usr/bin/env node" } scoped to the cli entry only (not the lib bundles — they'd break ESM import). Tsup supports per-entry options via the entry map but needs a small refactor to split cli from the lib entries; alternative is a post-build prepend-shebang script that targets only dist/cli.mjs. Going with the post-build script to avoid touching the lib build.
    • Post-build also chmodSync(dist/cli.mjs, 0o755) so even direct execution from the cloned repo works without node ./dist/cli.mjs.
    • Keep the source cli.js shebang-free so vitest/esbuild test imports still parse cleanly (the original constraint that motivated stripping the shebang is real).

    3. Repeated macOS Keychain prompts — partially confirmed, fixing both halves

    There are four independent keytar probe paths today:

    • cli.js:probeVaultState (called from setup-vault, status, login preflight)
    • checks/vault.js:tryKeychain (called from doctor)
    • vault.js:keyFromKeychain — cached via _unsealMaterialCache for the runtime path, but getAllUnsealMaterials() calls it un-cached every read
    • extension/src/auth/vault-passphrase.ts:probeKeytarAvailable (extension host probe via fork()'d child)

    Within one process the runtime path is fine after first hit; the prompt-spam comes from (a) separate CLI invocations each re-probing and (b) getAllUnsealMaterials not caching after the first negative probe in headless-Linux scenarios.

    Fix shape:

    • Cache the keychain probe result at module load in vault.js so getAllUnsealMaterials and getUnsealMaterial share state — one probe per process, regardless of how many vault reads happen.
    • Add PERPLEXITY_DISABLE_KEYCHAIN=1 as a documented escape hatch, honored in tryKeytar() (and mirrored in checks/vault.js:tryKeychain so the doctor agrees with the runtime path). When set, the keychain probe short-circuits to "unavailable" without ever calling keytar.getPassword().
    • Don't probe in pure-diagnostic flows when the user already provided PERPLEXITY_VAULT_PASSPHRASE — if env passphrase is set, the keychain probe is decoration and we can skip it on that branch in probeVaultState. Keeps the doctor honest while removing the gratuitous prompt.

    4. Manual login Chrome profile not persistent — confirmed; need a quick design call

    manual-login-runner.js:45-50 launches non-persistent. Daemon's headedBootstrap already uses launchPersistentContext(getActivePaths().browserData, ...). Two options for where the login runner's persistent profile lives — want your input here because either reads as "dedicated persistent browser profile" depending on what you meant:

    Option A — Share the daemon's ~/.perplexity-mcp/profiles/<name>/browser-data/. Single source of truth: Google SSO state from login carries directly into the daemon's headed bootstrap (the daemon's auth check might even succeed on first init without needing the vault round-trip). Risk: Chrome singleton-lock collision if the daemon is mid-headed-bootstrap when the user kicks off login — the existing clearStaleSingletonLocks() helper handles stale locks but not LIVE concurrent access. Mitigation: login runner waits up to N seconds for the lock to clear, falls back to option B if it can't.

    Option B — Dedicated ~/.perplexity-mcp/profiles/<name>/login-browser-data/. Clean lock separation, never conflicts with daemon. Trade-off: Google SSO state stays scoped to the login runner; the daemon picks up the resulting cookies through the vault path (which is already how it works today, just now with persisted Google session for next login).

    Which way do you want to go? Your wording "dedicated persistent browser profile" reads as Option B but A has bigger ergonomic upside if the lock case can be handled cleanly.

    Secondary note: this fix interacts with #5's Fix 2 (Phase 2 of the daemon currently uses non-persistent context too, so it discards the headed phase's fresh cf_clearance). If we go Option A here AND Fix 2 there, the daemon's Phase 2 reads the same browser-data dir as both the headed bootstrap AND the login runner — cookies and cf_clearance flow naturally through one disk profile.

    5. Hermes image_url rejection — acknowledged, not this repo

    Filed mentally. If you have the Hermes-side write-up somewhere, link it here so future readers can find it; otherwise no action.

    Process

    0.8.43 is the target release. Smoke matrix per docs/release-process.md covers Win11, macOS 14+, and Ubuntu 22+ — your bug 1+2 specifically need the macOS row to actually pass since Homebrew bin layout is the canonical reproducer.

    If you've already got patches in your fork for any of bugs 1–4, please send them as PRs and we'll review there — saves rebuilding the diff blind. Otherwise we'll cut the patches against main and push a draft PR within the next pass once #5's diagnostic data lands.

    Thanks again — the symlink + shebang pair was a clean catch and would have stayed hidden until the next macOS user filed.

  3. juangrukat commented on May 11, 2026

    @juangrukat
    ContributorAuthor

    I trimmed your repo pretty severely so can’t PR.
    I kept only the useful parts.

    The main problem was having to log in constantly, which should be addressed by previously noted.

  4. ARHAEEM commented on May 11, 2026

    @ARHAEEM
    Member

    Understood, no worries on the PR — the four diagnoses were enough to fix all of them. Locking in Option B (dedicated ~/.perplexity-mcp/profiles/<name>/login-browser-data/ for the login runner) per your "dedicated persistent browser profile" wording. The Option A upside disappeared once I looked closer at Chromium's user-data-dir locking — it's a per-process LIVE lock, not just a stale-file check, so even with clearStaleSingletonLocks() the login-while-daemon-is-mid-bootstrap window is a real failure mode. Option B sidesteps that entirely.

    Starting the patches now. Will ping when the 0.8.43 VSIX is ready for smoke; if you want to take it for a spin on your trimmed setup before the public release, I'll attach the artifact here.

    Thanks again for the focused reports — "having to log in constantly" was a real UX paper cut and fixing #6/4 alongside #5 means the daemon's headed bootstrap can stop being a CF-only step too.

  5. juangrukat commented on May 11, 2026

    @juangrukat
    ContributorAuthor

    No. need, I'll get pinged once you release, and my setup is working.

    Thanks for the work on this. I was going back and forth between browsers (perplexity, search). You've saved me time with this idea. thanks

  6. ARHAEEM commented on May 11, 2026

    @ARHAEEM
    Member

    Pinging as promised — all four bugs are patched and in PR #7 targeting 0.8.43.

    Quick summary of what landed:

    1. Symlink exit — isMainModule() now uses realpathSync on both sides, with fallback to the old comparison if realpath rejects. Both cli.js and index.ts updated.
    2. Shebang + exec mode — dist/cli.mjs gets #!/usr/bin/env node prepended and chmod 755 via a post-build script. No more silent-no-output on direct invocation.
    3. Keychain prompts — keytar module cached at module load; probeKeychainState() shared across cli.js and checks/vault.js (removes three duplicate inline probes); PERPLEXITY_DISABLE_KEYCHAIN=1 escape hatch for headless/CI.
    4. Login persistence — both login-runner.js and manual-login-runner.js now use launchPersistentContext(paths.loginBrowserData, ...) — dedicated login-browser-data/ dir, separate from the daemon's browser-data/ to avoid Chromium singleton-lock collisions (Option B as agreed). Soft-logout clears it so the next login starts fresh.

    130/130 tests pass, full typecheck clean. PR: #7

    Will attach the VSIX to the release notes once smoke passes — appreciate the patience.

  7. added a commit that references this issue on May 11, 2026
    0860ed9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions