From 018a67dbd6c3bc7a1d53f23a5d38f07e79f626ac Mon Sep 17 00:00:00 2001 From: Deva Annamaraju Date: Mon, 25 May 2026 23:20:48 +0530 Subject: [PATCH 1/2] fix cursor vpn auth diagnostics --- README.md | 23 + packages/cli/src/cursor.test.ts | 109 ++++- packages/cli/src/cursor.ts | 87 ++++ packages/registry/src/cursor-auth.test.ts | 116 ++++- packages/registry/src/cursor-auth.ts | 538 +++++++++++++++++++++- packages/registry/src/index.ts | 8 + 6 files changed, 854 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index d415f39..d0c48a4 100644 --- a/README.md +++ b/README.md @@ -196,6 +196,7 @@ Use these commands to manage Cursor authentication and the local cache that Toke tokenleak cursor login --name work tokenleak cursor status tokenleak cursor accounts --json +tokenleak cursor doctor tokenleak cursor switch work tokenleak cursor logout --name work tokenleak cursor logout --all --purge-cache @@ -238,6 +239,28 @@ bun packages/cli/dist/cli.js --provider cursor --format json - If `cursor status` is valid but `--list-providers` still shows Cursor as unavailable, run `tokenleak --provider cursor` once to sync the cache, then rerun `--list-providers`. - Cursor session tokens are stored in plaintext at `~/.config/tokenleak/cursor-credentials.json` (or under `TOKENLEAK_CURSOR_DIR`) with local-only file permissions. +#### Corporate VPN / protected network + +If `tokenleak cursor login` works off VPN but fails on a company protected VPN with a connection, proxy, or certificate error, run the token-free doctor first: + +```bash +tokenleak cursor doctor +``` + +For managed proxy networks, pass a Cursor-specific proxy or use your standard shell proxy variables: + +```bash +TOKENLEAK_CURSOR_PROXY=http://proxy.company:8080 tokenleak cursor doctor +``` + +For TLS inspection networks, export the company root CA as a PEM file and point Tokenleak at it: + +```bash +TOKENLEAK_CURSOR_CA_FILE=/path/company-root-ca.pem tokenleak cursor doctor --with-token +``` + +Tokenleak also honors `HTTPS_PROXY`, `HTTP_PROXY`, `NO_PROXY`, and `TOKENLEAK_CURSOR_TIMEOUT_MS` for Cursor API requests. `tokenleak cursor doctor --insecure-skip-tls-verify` exists only to prove that TLS inspection is the failure mode; do not use it for normal login or sync. + ### Date filtering By default, Tokenleak shows the last **90 days** of usage. diff --git a/packages/cli/src/cursor.test.ts b/packages/cli/src/cursor.test.ts index 780f6e1..3c8eb82 100644 --- a/packages/cli/src/cursor.test.ts +++ b/packages/cli/src/cursor.test.ts @@ -9,6 +9,7 @@ import { loadCursorCredentialsStore, removeAllCursorAccounts, resetCursorProviderState, + runCursorCommand, saveCursorCredentials, setActiveCursorAccount, shouldSyncCursorForRun, @@ -24,21 +25,47 @@ const SAMPLE_CSV = [ ].join('\n'); describe('cursor auth and sync helpers', () => { - const originalCursorDir = process.env['TOKENLEAK_CURSOR_DIR']; const originalFetch = globalThis.fetch; + const originalEnv = { ...process.env }; let tempRoot = ''; beforeEach(() => { tempRoot = mkdtempSync(join(tmpdir(), 'tokenleak-cursor-')); process.env['TOKENLEAK_CURSOR_DIR'] = tempRoot; + for (const key of [ + 'TOKENLEAK_CURSOR_PROXY', + 'TOKENLEAK_CURSOR_CA_FILE', + 'TOKENLEAK_CURSOR_TIMEOUT_MS', + 'HTTPS_PROXY', + 'https_proxy', + 'HTTP_PROXY', + 'http_proxy', + 'NO_PROXY', + 'no_proxy', + ]) { + delete process.env[key]; + } globalThis.fetch = originalFetch; }); afterEach(() => { - if (originalCursorDir === undefined) { - delete process.env['TOKENLEAK_CURSOR_DIR']; - } else { - process.env['TOKENLEAK_CURSOR_DIR'] = originalCursorDir; + for (const key of [ + 'TOKENLEAK_CURSOR_DIR', + 'TOKENLEAK_CURSOR_PROXY', + 'TOKENLEAK_CURSOR_CA_FILE', + 'TOKENLEAK_CURSOR_TIMEOUT_MS', + 'HTTPS_PROXY', + 'https_proxy', + 'HTTP_PROXY', + 'http_proxy', + 'NO_PROXY', + 'no_proxy', + ]) { + if (originalEnv[key] === undefined) { + delete process.env[key]; + } else { + process.env[key] = originalEnv[key]; + } } globalThis.fetch = originalFetch; rmSync(tempRoot, { recursive: true, force: true }); @@ -168,4 +195,76 @@ describe('cursor auth and sync helpers', () => { expect(existsSync(join(getCursorCacheDir(), 'usage.csv'))).toBe(false); expect(listCursorAccounts()).toEqual([]); }); + + test('cursor help includes doctor diagnostics', async () => { + let output = ''; + const originalWrite = process.stdout.write; + process.stdout.write = ((chunk: string | Uint8Array) => { + output += String(chunk); + return true; + }) as typeof process.stdout.write; + + try { + await runCursorCommand(['--help']); + } finally { + process.stdout.write = originalWrite; + } + + expect(output).toContain('tokenleak cursor doctor [--name