cursor-api-proxy 0.3.0 → 0.5.0

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 (64) hide show
  1. package/README.md +81 -34
  2. package/dist/cli/accounts.d.ts +18 -0
  3. package/dist/cli/accounts.js +149 -0
  4. package/dist/cli/accounts.js.map +1 -0
  5. package/dist/cli/args.d.ts +14 -0
  6. package/dist/cli/args.js +93 -0
  7. package/dist/cli/args.js.map +1 -0
  8. package/dist/cli/constants.d.ts +1 -0
  9. package/dist/cli/constants.js +4 -0
  10. package/dist/cli/constants.js.map +1 -0
  11. package/dist/cli/login.d.ts +1 -0
  12. package/dist/cli/login.js +143 -0
  13. package/dist/cli/login.js.map +1 -0
  14. package/dist/cli/reset-hwid.d.ts +24 -0
  15. package/dist/cli/reset-hwid.js +277 -0
  16. package/dist/cli/reset-hwid.js.map +1 -0
  17. package/dist/cli/usage.d.ts +31 -0
  18. package/dist/cli/usage.js +214 -0
  19. package/dist/cli/usage.js.map +1 -0
  20. package/dist/cli.d.ts +1 -4
  21. package/dist/cli.js +40 -35
  22. package/dist/cli.js.map +1 -1
  23. package/dist/lib/account-pool.d.ts +35 -0
  24. package/dist/lib/account-pool.js +143 -0
  25. package/dist/lib/account-pool.js.map +1 -0
  26. package/dist/lib/agent-runner.d.ts +2 -2
  27. package/dist/lib/agent-runner.js +18 -6
  28. package/dist/lib/agent-runner.js.map +1 -1
  29. package/dist/lib/anthropic.js +22 -11
  30. package/dist/lib/anthropic.js.map +1 -1
  31. package/dist/lib/config.d.ts +4 -0
  32. package/dist/lib/config.js +2 -0
  33. package/dist/lib/config.js.map +1 -1
  34. package/dist/lib/env.d.ts +4 -0
  35. package/dist/lib/env.js +68 -8
  36. package/dist/lib/env.js.map +1 -1
  37. package/dist/lib/handlers/anthropic-messages.js +60 -11
  38. package/dist/lib/handlers/anthropic-messages.js.map +1 -1
  39. package/dist/lib/handlers/chat-completions.js +54 -7
  40. package/dist/lib/handlers/chat-completions.js.map +1 -1
  41. package/dist/lib/handlers/models.d.ts +1 -0
  42. package/dist/lib/handlers/models.js +16 -6
  43. package/dist/lib/handlers/models.js.map +1 -1
  44. package/dist/lib/max-mode-preflight.d.ts +1 -1
  45. package/dist/lib/max-mode-preflight.js +9 -3
  46. package/dist/lib/max-mode-preflight.js.map +1 -1
  47. package/dist/lib/openai.d.ts +10 -0
  48. package/dist/lib/openai.js +53 -1
  49. package/dist/lib/openai.js.map +1 -1
  50. package/dist/lib/process.d.ts +6 -0
  51. package/dist/lib/process.js +71 -9
  52. package/dist/lib/process.js.map +1 -1
  53. package/dist/lib/request-listener.js +28 -4
  54. package/dist/lib/request-listener.js.map +1 -1
  55. package/dist/lib/request-log.d.ts +3 -0
  56. package/dist/lib/request-log.js +37 -0
  57. package/dist/lib/request-log.js.map +1 -1
  58. package/dist/lib/sanitize.d.ts +21 -0
  59. package/dist/lib/sanitize.js +79 -0
  60. package/dist/lib/sanitize.js.map +1 -0
  61. package/dist/lib/server.d.ts +6 -1
  62. package/dist/lib/server.js +75 -0
  63. package/dist/lib/server.js.map +1 -1
  64. package/package.json +5 -2
package/README.md CHANGED
@@ -84,7 +84,6 @@ To serve over HTTPS so browsers and clients trust the connection (e.g. `https://
84
84
  ```
85
85
 
86
86
  3. **Access the API** from any device on your tailnet:
87
-
88
87
  - Base URL: `https://macbook.tail4048eb.ts.net:8765/v1` (use your MagicDNS name and port)
89
88
  - Browsers will show a padlock; no certificate warnings when using Tailscale-issued certs.
90
89
 
@@ -105,7 +104,7 @@ This is an optional consumer-side example. `openai` is not a dependency of `curs
105
104
  import OpenAI from "openai";
106
105
  import { getOpenAIOptionsAsync } from "cursor-api-proxy";
107
106
 
108
- const opts = await getOpenAIOptionsAsync(); // starts proxy if needed
107
+ const opts = await getOpenAIOptionsAsync(); // starts proxy if needed
109
108
  const client = new OpenAI(opts);
110
109
 
111
110
  const completion = await client.chat.completions.create({
@@ -122,7 +121,7 @@ For a sync config without auto-start, use `getOpenAIOptions()` and ensure the pr
122
121
  ```js
123
122
  import { createCursorProxyClient } from "cursor-api-proxy";
124
123
 
125
- const proxy = createCursorProxyClient(); // proxy starts on first request if needed
124
+ const proxy = createCursorProxyClient(); // proxy starts on first request if needed
126
125
  const data = await proxy.chatCompletionsCreate({
127
126
  model: "auto",
128
127
  messages: [{ role: "user", content: "Hello" }],
@@ -144,40 +143,43 @@ const client = new OpenAI({
144
143
 
145
144
  ### Endpoints
146
145
 
147
- | Method | Path | Description |
148
- |--------|------|-------------|
149
- | GET | `/health` | Server and config info |
150
- | GET | `/v1/models` | List Cursor models (from `agent --list-models`) |
151
- | POST | `/v1/chat/completions` | Chat completion (OpenAI shape; supports `stream: true`) |
152
- | POST | `/v1/messages` | Anthropic Messages API (used by Claude Code; supports `stream: true`) |
146
+ | Method | Path | Description |
147
+ | ------ | ---------------------- | --------------------------------------------------------------------- |
148
+ | GET | `/health` | Server and config info |
149
+ | GET | `/v1/models` | List Cursor models (from `agent --list-models`) |
150
+ | POST | `/v1/chat/completions` | Chat completion (OpenAI shape; supports `stream: true`) |
151
+ | POST | `/v1/messages` | Anthropic Messages API (used by Claude Code; supports `stream: true`) |
153
152
 
154
153
  ## Environment variables
155
154
 
156
155
  Environment handling is centralized in one module. Aliases, defaults, path resolution, platform fallbacks, and `--tailscale` host behavior are resolved consistently before the server starts.
157
156
 
158
- | Variable | Default | Description |
159
- |----------|---------|-------------|
160
- | `CURSOR_BRIDGE_HOST` | `127.0.0.1` | Bind address |
161
- | `CURSOR_BRIDGE_PORT` | `8765` | Port |
162
- | `CURSOR_BRIDGE_API_KEY` | — | If set, require `Authorization: Bearer <key>` on requests |
163
- | `CURSOR_BRIDGE_WORKSPACE` | process cwd | Workspace directory for Cursor CLI |
164
- | `CURSOR_BRIDGE_MODE` | — | Ignored; proxy always runs in **ask** (chat-only) mode so the CLI never creates or edits files. |
165
- | `CURSOR_BRIDGE_DEFAULT_MODEL` | `auto` | Default model when request omits one |
166
- | `CURSOR_BRIDGE_STRICT_MODEL` | `true` | Use last requested model when none specified |
167
- | `CURSOR_BRIDGE_FORCE` | `false` | Pass `--force` to Cursor CLI |
168
- | `CURSOR_BRIDGE_APPROVE_MCPS` | `false` | Pass `--approve-mcps` to Cursor CLI |
169
- | `CURSOR_BRIDGE_TIMEOUT_MS` | `300000` | Timeout per completion (ms) |
170
- | `CURSOR_BRIDGE_TLS_CERT` | — | Path to TLS certificate file (e.g. Tailscale cert). Use with `CURSOR_BRIDGE_TLS_KEY` for HTTPS. |
171
- | `CURSOR_BRIDGE_TLS_KEY` | — | Path to TLS private key file. Use with `CURSOR_BRIDGE_TLS_CERT` for HTTPS. |
172
- | `CURSOR_BRIDGE_SESSIONS_LOG` | `~/.cursor-api-proxy/sessions.log` | Path to log file; each request is appended as a line (timestamp, method, path, IP, status). |
173
- | `CURSOR_BRIDGE_CHAT_ONLY_WORKSPACE` | `true` | When `true` (default), the CLI runs in an empty temp dir so it **cannot read or write your project**; pure chat only. Set to `false` to pass the real workspace (e.g. for `X-Cursor-Workspace`). |
174
- | `CURSOR_BRIDGE_VERBOSE` | `false` | When `true`, print full request messages and response content to stdout for every completion (both stream and sync). |
175
- | `CURSOR_BRIDGE_MAX_MODE` | `false` | When `true`, enable Cursor **Max Mode** for all requests (larger context window, higher tool-call limits). The proxy writes `maxMode: true` to `cli-config.json` before each run. Works when using `CURSOR_AGENT_NODE`/`CURSOR_AGENT_SCRIPT` or the default Windows `.cmd` layout (node.exe + index.js next to agent.cmd). |
176
- | `CURSOR_AGENT_BIN` | `agent` | Path to Cursor CLI binary. Alias precedence: `CURSOR_AGENT_BIN`, then `CURSOR_CLI_BIN`, then `CURSOR_CLI_PATH`. |
177
- | `CURSOR_AGENT_NODE` | — | **(Windows)** Path to Node.js executable. When set together with `CURSOR_AGENT_SCRIPT`, spawns Node directly instead of going through cmd.exe, bypassing the ~8191 character command line limit. |
178
- | `CURSOR_AGENT_SCRIPT` | — | **(Windows)** Path to the agent script (e.g. `agent.cmd` or the underlying `.js`). Use with `CURSOR_AGENT_NODE` to bypass cmd.exe for long prompts. |
157
+ | Variable | Default | Description |
158
+ | ----------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
159
+ | `CURSOR_BRIDGE_HOST` | `127.0.0.1` | Bind address |
160
+ | `CURSOR_BRIDGE_PORT` | `8765` | Port |
161
+ | `CURSOR_BRIDGE_API_KEY` | — | If set, require `Authorization: Bearer <key>` on requests |
162
+ | `CURSOR_BRIDGE_WORKSPACE` | process cwd | Workspace directory for Cursor CLI |
163
+ | `CURSOR_BRIDGE_MODE` | — | Ignored; proxy always runs in **ask** (chat-only) mode so the CLI never creates or edits files. |
164
+ | `CURSOR_BRIDGE_DEFAULT_MODEL` | `auto` | Default model when request omits one |
165
+ | `CURSOR_BRIDGE_STRICT_MODEL` | `true` | Use last requested model when none specified |
166
+ | `CURSOR_BRIDGE_FORCE` | `false` | Pass `--force` to Cursor CLI |
167
+ | `CURSOR_BRIDGE_APPROVE_MCPS` | `false` | Pass `--approve-mcps` to Cursor CLI |
168
+ | `CURSOR_BRIDGE_TIMEOUT_MS` | `300000` | Timeout per completion (ms) |
169
+ | `CURSOR_BRIDGE_TLS_CERT` | — | Path to TLS certificate file (e.g. Tailscale cert). Use with `CURSOR_BRIDGE_TLS_KEY` for HTTPS. |
170
+ | `CURSOR_BRIDGE_TLS_KEY` | — | Path to TLS private key file. Use with `CURSOR_BRIDGE_TLS_CERT` for HTTPS. |
171
+ | `CURSOR_BRIDGE_SESSIONS_LOG` | `~/.cursor-api-proxy/sessions.log` | Path to log file; each request is appended as a line (timestamp, method, path, IP, status). |
172
+ | `CURSOR_BRIDGE_CHAT_ONLY_WORKSPACE` | `true` | When `true` (default), the CLI runs in an empty temp dir so it **cannot read or write your project**; pure chat only. Set to `false` to pass the real workspace (e.g. for `X-Cursor-Workspace`). |
173
+ | `CURSOR_BRIDGE_VERBOSE` | `false` | When `true`, print full request messages and response content to stdout for every completion (both stream and sync). |
174
+ | `CURSOR_BRIDGE_MAX_MODE` | `false` | When `true`, enable Cursor **Max Mode** for all requests (larger context window, higher tool-call limits). The proxy writes `maxMode: true` to `cli-config.json` before each run. Works when using `CURSOR_AGENT_NODE`/`CURSOR_AGENT_SCRIPT` or the default Windows `.cmd` layout (node.exe + index.js next to agent.cmd). |
175
+ | `CURSOR_CONFIG_DIRS` | — | Comma-separated list of configuration directories (e.g. `/home/user/.config/cursor-agent-1,/home/user/.config/cursor-agent-2`). Used for round-robin rotation between multiple accounts to distribute load and avoid rate limits. |
176
+ | `CURSOR_BRIDGE_MULTI_PORT` | `false` | When `true` and `CURSOR_CONFIG_DIRS` is set, instead of a single server doing round-robin, the proxy spawns a separate server for each configuration directory on incrementing ports (starting from `CURSOR_BRIDGE_PORT`). |
177
+ | `CURSOR_AGENT_BIN` | `agent` | Path to Cursor CLI binary. Alias precedence: `CURSOR_AGENT_BIN`, then `CURSOR_CLI_BIN`, then `CURSOR_CLI_PATH`. |
178
+ | `CURSOR_AGENT_NODE` | — | **(Windows)** Path to Node.js executable. When set together with `CURSOR_AGENT_SCRIPT`, spawns Node directly instead of going through cmd.exe, bypassing the ~8191 character command line limit. |
179
+ | `CURSOR_AGENT_SCRIPT` | — | **(Windows)** Path to the agent script (e.g. `agent.cmd` or the underlying `.js`). Use with `CURSOR_AGENT_NODE` to bypass cmd.exe for long prompts. |
179
180
 
180
181
  Notes:
182
+
181
183
  - `--tailscale` changes the default host to `0.0.0.0` only when `CURSOR_BRIDGE_HOST` is not already set.
182
184
  - Relative paths such as `CURSOR_BRIDGE_WORKSPACE`, `CURSOR_BRIDGE_SESSIONS_LOG`, `CURSOR_BRIDGE_TLS_CERT`, and `CURSOR_BRIDGE_TLS_KEY` are resolved from the current working directory.
183
185
 
@@ -195,13 +197,58 @@ set CURSOR_AGENT_SCRIPT=C:\path\to\Cursor\resources\agent\agent.cmd
195
197
 
196
198
  CLI flags:
197
199
 
198
- | Flag | Description |
199
- |------|-------------|
200
- | `--tailscale` | Bind to `0.0.0.0` for access from tailnet/LAN (unless `CURSOR_BRIDGE_HOST` is already set) |
201
- | `-h`, `--help` | Show CLI usage |
200
+ | Flag | Description |
201
+ | -------------- | ------------------------------------------------------------------------------------------ |
202
+ | `--tailscale` | Bind to `0.0.0.0` for access from tailnet/LAN (unless `CURSOR_BRIDGE_HOST` is already set) |
203
+ | `-h`, `--help` | Show CLI usage |
202
204
 
203
205
  Optional per-request override: send header `X-Cursor-Workspace: <path>` to use a different workspace for that request.
204
206
 
207
+ ## Multi-Account Setup
208
+
209
+ You can use multiple Cursor accounts to distribute load and avoid hitting usage limits. The proxy now includes a built-in account manager that makes this very easy.
210
+
211
+ ### 1. Adding Accounts (Easy Method)
212
+
213
+ You can add new accounts using the CLI `login` command. This will launch the Cursor CLI login process in an isolated profile directory (`~/.cursor-api-proxy/accounts/`).
214
+
215
+ ```bash
216
+ npx cursor-api-proxy login account1
217
+ ```
218
+
219
+ _(A clean, incognito browser window will open for you to log into Cursor. Once done, the session is saved)._
220
+
221
+ Repeat this for as many accounts as you want:
222
+
223
+ ```bash
224
+ npx cursor-api-proxy login account2
225
+ npx cursor-api-proxy login account3
226
+ ```
227
+
228
+ **Auto-Discovery:** When you start the proxy server normally (`npx cursor-api-proxy`), it will automatically find all accounts in your `~/.cursor-api-proxy/accounts/` directory and include them in the rotation pool!
229
+
230
+ ### 2. Manual Config Directories
231
+
232
+ If you already have separate configuration folders (or want to specify them explicitly), you can override auto-discovery using the `CURSOR_CONFIG_DIRS` environment variable:
233
+
234
+ ```bash
235
+ CURSOR_CONFIG_DIRS=/path/to/cursor-agent-1,/path/to/cursor-agent-2 npm start
236
+ ```
237
+
238
+ ### 3. Modes of operation
239
+
240
+ **A. Single Port, Round-Robin Rotation (Default)**
241
+ In this mode, the proxy listens on one port and rotates through the available accounts for each request, selecting the least busy account automatically. This is active by default when multiple accounts are found.
242
+
243
+ **B. Multi-Port (One Server Per Account)**
244
+ If you want granular control (for example, to explicitly assign specific clients to specific accounts), you can use multi-port mode. The proxy will spawn multiple instances on incrementing ports, starting from `CURSOR_BRIDGE_PORT`.
245
+
246
+ ```bash
247
+ CURSOR_BRIDGE_MULTI_PORT=true CURSOR_BRIDGE_PORT=8765 npm start
248
+ ```
249
+
250
+ _Result: account1 is on 8765, account2 is on 8766, etc._
251
+
205
252
  ## Streaming
206
253
 
207
254
  The proxy supports `stream: true` on `POST /v1/chat/completions` and `POST /v1/messages`. It returns Server-Sent Events (SSE) in OpenAI’s streaming format. Cursor CLI emits incremental deltas plus a final full message; the proxy deduplicates output so clients receive each chunk only once.
@@ -0,0 +1,18 @@
1
+ export interface AccountInfo {
2
+ name: string;
3
+ configDir: string;
4
+ authenticated: boolean;
5
+ email?: string;
6
+ displayName?: string;
7
+ authId?: string;
8
+ plan?: string;
9
+ subscriptionStatus?: string;
10
+ expiresAt?: string;
11
+ }
12
+ /**
13
+ * Reads authentication and plan metadata from a saved account directory.
14
+ * Never throws — returns `authenticated: false` on any read/parse error.
15
+ */
16
+ export declare function readAccountInfo(name: string, configDir: string): AccountInfo;
17
+ export declare function handleAccountsList(): Promise<void>;
18
+ export declare function handleLogout(accountName: string): Promise<void>;
@@ -0,0 +1,149 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { ACCOUNTS_DIR } from "./constants.js";
4
+ import { readCachedToken, readKeychainToken, tokenSub, fetchAccountUsage, fetchStripeProfile, formatUsageSummary, describePlan, } from "./usage.js";
5
+ // ---------------------------------------------------------------------------
6
+ // Helpers
7
+ // ---------------------------------------------------------------------------
8
+ /**
9
+ * Reads authentication and plan metadata from a saved account directory.
10
+ * Never throws — returns `authenticated: false` on any read/parse error.
11
+ */
12
+ export function readAccountInfo(name, configDir) {
13
+ const info = { name, configDir, authenticated: false };
14
+ const configFile = path.join(configDir, "cli-config.json");
15
+ if (!fs.existsSync(configFile))
16
+ return info;
17
+ try {
18
+ const raw = JSON.parse(fs.readFileSync(configFile, "utf-8"));
19
+ if (raw.authInfo) {
20
+ info.authenticated = true;
21
+ info.email = raw.authInfo.email;
22
+ info.displayName = raw.authInfo.displayName;
23
+ info.authId = raw.authInfo.authId;
24
+ }
25
+ }
26
+ catch {
27
+ // malformed config — treat as unauthenticated
28
+ }
29
+ const statsigFile = path.join(configDir, "statsig-cache.json");
30
+ if (!fs.existsSync(statsigFile))
31
+ return info;
32
+ try {
33
+ const statsigRaw = JSON.parse(fs.readFileSync(statsigFile, "utf-8"));
34
+ if (!statsigRaw.data)
35
+ return info;
36
+ const statsig = JSON.parse(statsigRaw.data);
37
+ const custom = statsig?.user?.custom;
38
+ if (!custom)
39
+ return info;
40
+ if (custom.isEnterpriseUser) {
41
+ info.plan = "Enterprise";
42
+ }
43
+ else if (custom.stripeSubscriptionStatus === "active") {
44
+ info.plan = "Pro";
45
+ }
46
+ else {
47
+ info.plan = "Free";
48
+ }
49
+ info.subscriptionStatus = custom.stripeSubscriptionStatus;
50
+ if (custom.stripeMembershipExpiration) {
51
+ info.expiresAt = new Date(custom.stripeMembershipExpiration).toLocaleDateString();
52
+ }
53
+ }
54
+ catch {
55
+ // malformed statsig cache — skip plan info
56
+ }
57
+ return info;
58
+ }
59
+ // ---------------------------------------------------------------------------
60
+ // Commands
61
+ // ---------------------------------------------------------------------------
62
+ export async function handleAccountsList() {
63
+ if (!fs.existsSync(ACCOUNTS_DIR)) {
64
+ console.log("No accounts found. Use 'cursor-api-proxy login' to add one.");
65
+ return;
66
+ }
67
+ const entries = fs.readdirSync(ACCOUNTS_DIR, { withFileTypes: true });
68
+ const names = entries.filter((e) => e.isDirectory()).map((e) => e.name);
69
+ if (names.length === 0) {
70
+ console.log("No accounts found. Use 'cursor-api-proxy login' to add one.");
71
+ return;
72
+ }
73
+ console.log("šŸ”‘ Cursor Accounts:\n");
74
+ // Try to find an available token (per-account cache first, shared keychain fallback)
75
+ const keychainToken = readKeychainToken();
76
+ for (let i = 0; i < names.length; i++) {
77
+ const name = names[i];
78
+ const configDir = path.join(ACCOUNTS_DIR, name);
79
+ const info = readAccountInfo(name, configDir);
80
+ console.log(` ${i + 1}. ${name}`);
81
+ if (info.authenticated) {
82
+ // Per-account cached token wins. Fall back to keychain ONLY if its JWT
83
+ // sub matches this account's authId (prevents showing another account's data).
84
+ const cachedToken = readCachedToken(configDir);
85
+ const keychainMatchesAccount = !!keychainToken &&
86
+ !!info.authId &&
87
+ tokenSub(keychainToken) === info.authId;
88
+ const token = cachedToken ?? (keychainMatchesAccount ? keychainToken : undefined);
89
+ // Fetch live data before printing so we can decide what to show
90
+ let liveProfile = null;
91
+ let liveUsage = null;
92
+ if (token) {
93
+ try {
94
+ [liveUsage, liveProfile] = await Promise.all([
95
+ fetchAccountUsage(token),
96
+ fetchStripeProfile(token),
97
+ ]);
98
+ }
99
+ catch {
100
+ /* ignore transient fetch errors */
101
+ }
102
+ }
103
+ if (info.email) {
104
+ const display = info.displayName ? ` (${info.displayName})` : "";
105
+ console.log(` ļæ½ ${info.email}${display}`);
106
+ }
107
+ // Show static plan only when live data isn't available (avoids contradictions)
108
+ if (info.plan && !liveProfile) {
109
+ const canceled = info.subscriptionStatus === "canceled" ? " Ā· canceled" : "";
110
+ const expiry = info.expiresAt ? ` Ā· expires ${info.expiresAt}` : "";
111
+ console.log(` šŸ“Š ${info.plan}${canceled}${expiry}`);
112
+ }
113
+ console.log(` āœ… Authenticated`);
114
+ if (liveProfile) {
115
+ console.log(` šŸ’³ ${describePlan(liveProfile)}`);
116
+ }
117
+ if (liveUsage) {
118
+ for (const line of formatUsageSummary(liveUsage))
119
+ console.log(line);
120
+ }
121
+ }
122
+ else {
123
+ console.log(` āš ļø Not authenticated`);
124
+ }
125
+ console.log("");
126
+ }
127
+ console.log("Tip: run 'cursor-api-proxy logout <name>' to remove an account.");
128
+ }
129
+ export async function handleLogout(accountName) {
130
+ if (!accountName) {
131
+ console.error("āŒ Error: Please specify the account name to remove.");
132
+ console.error("Usage: cursor-api-proxy logout <account-name>");
133
+ process.exit(1);
134
+ }
135
+ const configDir = path.join(ACCOUNTS_DIR, accountName);
136
+ if (!fs.existsSync(configDir)) {
137
+ console.error(`āŒ Account '${accountName}' not found.`);
138
+ process.exit(1);
139
+ }
140
+ try {
141
+ fs.rmSync(configDir, { recursive: true, force: true });
142
+ console.log(`āœ… Account '${accountName}' removed.`);
143
+ }
144
+ catch (err) {
145
+ console.error(`āŒ Error removing account:`, err);
146
+ process.exit(1);
147
+ }
148
+ }
149
+ //# sourceMappingURL=accounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/cli/accounts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export type ParsedArgs = {
2
+ tailscale: boolean;
3
+ help: boolean;
4
+ login: boolean;
5
+ accountsList: boolean;
6
+ logout: boolean;
7
+ accountName: string;
8
+ proxies: string[];
9
+ resetHwid: boolean;
10
+ deepClean: boolean;
11
+ dryRun: boolean;
12
+ };
13
+ export declare function parseArgs(argv: string[]): ParsedArgs;
14
+ export declare function printHelp(version: string): void;
@@ -0,0 +1,93 @@
1
+ export function parseArgs(argv) {
2
+ let tailscale = false;
3
+ let help = false;
4
+ let login = false;
5
+ let accountsList = false;
6
+ let logout = false;
7
+ let accountName = "";
8
+ let proxies = [];
9
+ let resetHwid = false;
10
+ let deepClean = false;
11
+ let dryRun = false;
12
+ for (let i = 0; i < argv.length; i++) {
13
+ const arg = argv[i];
14
+ if (arg === "login" || arg === "add-account") {
15
+ login = true;
16
+ if (i + 1 < argv.length && !argv[i + 1].startsWith("-")) {
17
+ accountName = argv[++i];
18
+ }
19
+ continue;
20
+ }
21
+ if (arg === "logout" || arg === "remove-account") {
22
+ logout = true;
23
+ if (i + 1 < argv.length && !argv[i + 1].startsWith("-")) {
24
+ accountName = argv[++i];
25
+ }
26
+ continue;
27
+ }
28
+ if (arg === "accounts" || arg === "list-accounts") {
29
+ accountsList = true;
30
+ continue;
31
+ }
32
+ if (arg === "reset-hwid" || arg === "reset") {
33
+ resetHwid = true;
34
+ continue;
35
+ }
36
+ if (arg === "--deep-clean") {
37
+ deepClean = true;
38
+ continue;
39
+ }
40
+ if (arg === "--dry-run") {
41
+ dryRun = true;
42
+ continue;
43
+ }
44
+ if (arg === "--tailscale") {
45
+ tailscale = true;
46
+ continue;
47
+ }
48
+ if (arg === "--help" || arg === "-h") {
49
+ help = true;
50
+ continue;
51
+ }
52
+ if (arg.startsWith("--proxy=")) {
53
+ proxies = arg
54
+ .slice("--proxy=".length)
55
+ .split(",")
56
+ .map((p) => p.trim())
57
+ .filter(Boolean);
58
+ continue;
59
+ }
60
+ throw new Error(`Unknown argument: ${arg}`);
61
+ }
62
+ return {
63
+ tailscale,
64
+ help,
65
+ login,
66
+ accountsList,
67
+ logout,
68
+ accountName,
69
+ proxies,
70
+ resetHwid,
71
+ deepClean,
72
+ dryRun,
73
+ };
74
+ }
75
+ export function printHelp(version) {
76
+ console.log(`cursor-api-proxy v${version}`);
77
+ console.log("");
78
+ console.log("Usage:");
79
+ console.log(" cursor-api-proxy [options]");
80
+ console.log("");
81
+ console.log("Commands:");
82
+ console.log(" login [name] Log into a Cursor account (saved to ~/.cursor-api-proxy/accounts/)");
83
+ console.log(" login [name] --proxy=... Same, but open Chrome through a random proxy from a comma-separated list");
84
+ console.log(" logout <name> Remove a saved Cursor account");
85
+ console.log(" accounts List saved accounts with plan info");
86
+ console.log(" reset-hwid Reset Cursor machine/telemetry IDs (anti-ban)");
87
+ console.log(" reset-hwid --deep-clean Also wipe session storage and cookies");
88
+ console.log("");
89
+ console.log("Options:");
90
+ console.log(" --tailscale Bind to 0.0.0.0 for tailnet/LAN access");
91
+ console.log(" -h, --help Show this help message");
92
+ }
93
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare const ACCOUNTS_DIR: string;
@@ -0,0 +1,4 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ export const ACCOUNTS_DIR = path.join(process.env.HOME || process.env.USERPROFILE || os.homedir(), ".cursor-api-proxy", "accounts");
4
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/cli/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CACnC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,OAAO,EAAE,EAC3D,mBAAmB,EACnB,UAAU,CACX,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function handleLogin(accountName: string, proxies?: string[]): Promise<void>;
@@ -0,0 +1,143 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { spawn } from "node:child_process";
4
+ import { launch as launchChrome } from "chrome-launcher";
5
+ import { loadEnvConfig } from "../lib/env.js";
6
+ import { ACCOUNTS_DIR } from "./constants.js";
7
+ import { readKeychainToken, writeCachedToken } from "./usage.js";
8
+ // ---------------------------------------------------------------------------
9
+ // Internal helpers
10
+ // ---------------------------------------------------------------------------
11
+ const LOGIN_URL_RE = /(https:\/\/cursor\.com\/loginDeepControl.*?redirectTarget=cli)/s;
12
+ async function openIncognito(url, proxies) {
13
+ const chromeFlags = [
14
+ "--incognito",
15
+ "--new-window",
16
+ "--no-first-run",
17
+ "--no-default-browser-check",
18
+ "--disable-translate",
19
+ ];
20
+ if (proxies.length > 0) {
21
+ const proxy = proxies[Math.floor(Math.random() * proxies.length)];
22
+ chromeFlags.push(`--proxy-server=${proxy}`);
23
+ console.log(`šŸ”€ Using proxy: ${proxy}`);
24
+ }
25
+ try {
26
+ await launchChrome({
27
+ startingUrl: url,
28
+ chromeFlags,
29
+ ignoreDefaultFlags: true,
30
+ handleSIGINT: false,
31
+ logLevel: "silent",
32
+ });
33
+ }
34
+ catch (e) {
35
+ const msg = e instanceof Error ? e.message : String(e);
36
+ console.log(`\n🌐 Could not open Chrome automatically: ${msg}`);
37
+ console.log(`Please open this URL in a private/incognito window:\n${url}\n`);
38
+ }
39
+ }
40
+ // ---------------------------------------------------------------------------
41
+ // Command
42
+ // ---------------------------------------------------------------------------
43
+ export async function handleLogin(accountName, proxies = []) {
44
+ const envCfg = loadEnvConfig();
45
+ const name = accountName || `account-${Date.now().toString().slice(-4)}`;
46
+ const configDir = path.join(ACCOUNTS_DIR, name);
47
+ const dirWasNew = !fs.existsSync(configDir);
48
+ fs.mkdirSync(ACCOUNTS_DIR, { recursive: true });
49
+ fs.mkdirSync(configDir, { recursive: true });
50
+ console.log(`šŸ”‘ Logging into Cursor account: ${name}`);
51
+ console.log(`šŸ“ Config: ${configDir}`);
52
+ console.log("");
53
+ console.log("A Chrome incognito window will open — complete the login there.");
54
+ console.log("");
55
+ return new Promise((resolve, reject) => {
56
+ let browserOpened = false;
57
+ let stdoutBuffer = "";
58
+ const cleanupDir = () => {
59
+ if (!dirWasNew)
60
+ return;
61
+ try {
62
+ fs.rmSync(configDir, { recursive: true, force: true });
63
+ }
64
+ catch {
65
+ // best-effort
66
+ }
67
+ };
68
+ const child = spawn(envCfg.agentBin, ["login"], {
69
+ stdio: ["inherit", "pipe", "pipe"],
70
+ env: {
71
+ ...process.env,
72
+ CURSOR_CONFIG_DIR: configDir,
73
+ NO_OPEN_BROWSER: "1",
74
+ },
75
+ });
76
+ // Remove all signal handlers once the child exits (success or failure)
77
+ const onCancel = (signal) => {
78
+ child.kill();
79
+ cleanupDir();
80
+ if (signal === "SIGINT")
81
+ console.log("\n\nāŒ Login cancelled.");
82
+ process.exit(0);
83
+ };
84
+ const onSigint = () => onCancel("SIGINT");
85
+ const onSigterm = () => onCancel("SIGTERM");
86
+ const onSighup = () => onCancel("SIGHUP");
87
+ const removeSignalHandlers = () => {
88
+ process.removeListener("SIGINT", onSigint);
89
+ process.removeListener("SIGTERM", onSigterm);
90
+ process.removeListener("SIGHUP", onSighup);
91
+ };
92
+ process.once("SIGINT", onSigint);
93
+ process.once("SIGTERM", onSigterm);
94
+ process.once("SIGHUP", onSighup);
95
+ child.stdout?.on("data", (data) => {
96
+ const text = data.toString();
97
+ process.stdout.write(text);
98
+ stdoutBuffer += text;
99
+ // The agent prints the login URL across multiple chunks — buffer until complete
100
+ if (!browserOpened &&
101
+ stdoutBuffer.includes("https://cursor.com/loginDeepControl")) {
102
+ const match = stdoutBuffer.match(LOGIN_URL_RE);
103
+ if (match?.[1]) {
104
+ const url = match[1].replace(/\s+/g, "");
105
+ openIncognito(url, proxies).catch(() => { });
106
+ browserOpened = true;
107
+ }
108
+ }
109
+ });
110
+ child.stderr?.on("data", (data) => {
111
+ process.stderr.write(data.toString());
112
+ });
113
+ child.on("error", (err) => {
114
+ removeSignalHandlers();
115
+ cleanupDir();
116
+ if (err.code === "ENOENT") {
117
+ console.error(`āŒ Could not find '${envCfg.agentBin}'. Make sure the Cursor CLI is installed.`);
118
+ }
119
+ else {
120
+ console.error("āŒ Error launching agent login:", err);
121
+ }
122
+ reject(err);
123
+ });
124
+ child.on("exit", (code) => {
125
+ removeSignalHandlers();
126
+ if (code === 0) {
127
+ // Immediately cache the keychain token for this account so that
128
+ // 'accounts list' can show live usage without needing a prior request.
129
+ const token = readKeychainToken();
130
+ if (token)
131
+ writeCachedToken(configDir, token);
132
+ console.log(`\nāœ… Account '${name}' saved — it will be auto-discovered when you start the proxy.`);
133
+ resolve();
134
+ }
135
+ else {
136
+ cleanupDir();
137
+ console.error(`\nāŒ Login failed (exit code ${code}).`);
138
+ reject(new Error(`Login failed with code ${code}`));
139
+ }
140
+ });
141
+ });
142
+ }
143
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/cli/login.ts"],"names":[],"mappings":"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"}