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.
- package/README.md +81 -34
- package/dist/cli/accounts.d.ts +18 -0
- package/dist/cli/accounts.js +149 -0
- package/dist/cli/accounts.js.map +1 -0
- package/dist/cli/args.d.ts +14 -0
- package/dist/cli/args.js +93 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/constants.d.ts +1 -0
- package/dist/cli/constants.js +4 -0
- package/dist/cli/constants.js.map +1 -0
- package/dist/cli/login.d.ts +1 -0
- package/dist/cli/login.js +143 -0
- package/dist/cli/login.js.map +1 -0
- package/dist/cli/reset-hwid.d.ts +24 -0
- package/dist/cli/reset-hwid.js +277 -0
- package/dist/cli/reset-hwid.js.map +1 -0
- package/dist/cli/usage.d.ts +31 -0
- package/dist/cli/usage.js +214 -0
- package/dist/cli/usage.js.map +1 -0
- package/dist/cli.d.ts +1 -4
- package/dist/cli.js +40 -35
- package/dist/cli.js.map +1 -1
- package/dist/lib/account-pool.d.ts +35 -0
- package/dist/lib/account-pool.js +143 -0
- package/dist/lib/account-pool.js.map +1 -0
- package/dist/lib/agent-runner.d.ts +2 -2
- package/dist/lib/agent-runner.js +18 -6
- package/dist/lib/agent-runner.js.map +1 -1
- package/dist/lib/anthropic.js +22 -11
- package/dist/lib/anthropic.js.map +1 -1
- package/dist/lib/config.d.ts +4 -0
- package/dist/lib/config.js +2 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/env.d.ts +4 -0
- package/dist/lib/env.js +68 -8
- package/dist/lib/env.js.map +1 -1
- package/dist/lib/handlers/anthropic-messages.js +60 -11
- package/dist/lib/handlers/anthropic-messages.js.map +1 -1
- package/dist/lib/handlers/chat-completions.js +54 -7
- package/dist/lib/handlers/chat-completions.js.map +1 -1
- package/dist/lib/handlers/models.d.ts +1 -0
- package/dist/lib/handlers/models.js +16 -6
- package/dist/lib/handlers/models.js.map +1 -1
- package/dist/lib/max-mode-preflight.d.ts +1 -1
- package/dist/lib/max-mode-preflight.js +9 -3
- package/dist/lib/max-mode-preflight.js.map +1 -1
- package/dist/lib/openai.d.ts +10 -0
- package/dist/lib/openai.js +53 -1
- package/dist/lib/openai.js.map +1 -1
- package/dist/lib/process.d.ts +6 -0
- package/dist/lib/process.js +71 -9
- package/dist/lib/process.js.map +1 -1
- package/dist/lib/request-listener.js +28 -4
- package/dist/lib/request-listener.js.map +1 -1
- package/dist/lib/request-log.d.ts +3 -0
- package/dist/lib/request-log.js +37 -0
- package/dist/lib/request-log.js.map +1 -1
- package/dist/lib/sanitize.d.ts +21 -0
- package/dist/lib/sanitize.js +79 -0
- package/dist/lib/sanitize.js.map +1 -0
- package/dist/lib/server.d.ts +6 -1
- package/dist/lib/server.js +75 -0
- package/dist/lib/server.js.map +1 -1
- 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();
|
|
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();
|
|
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
|
|
148
|
-
|
|
149
|
-
| GET
|
|
150
|
-
| GET
|
|
151
|
-
| POST
|
|
152
|
-
| POST
|
|
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
|
|
159
|
-
|
|
160
|
-
| `CURSOR_BRIDGE_HOST`
|
|
161
|
-
| `CURSOR_BRIDGE_PORT`
|
|
162
|
-
| `CURSOR_BRIDGE_API_KEY`
|
|
163
|
-
| `CURSOR_BRIDGE_WORKSPACE`
|
|
164
|
-
| `CURSOR_BRIDGE_MODE`
|
|
165
|
-
| `CURSOR_BRIDGE_DEFAULT_MODEL`
|
|
166
|
-
| `CURSOR_BRIDGE_STRICT_MODEL`
|
|
167
|
-
| `CURSOR_BRIDGE_FORCE`
|
|
168
|
-
| `CURSOR_BRIDGE_APPROVE_MCPS`
|
|
169
|
-
| `CURSOR_BRIDGE_TIMEOUT_MS`
|
|
170
|
-
| `CURSOR_BRIDGE_TLS_CERT`
|
|
171
|
-
| `CURSOR_BRIDGE_TLS_KEY`
|
|
172
|
-
| `CURSOR_BRIDGE_SESSIONS_LOG`
|
|
173
|
-
| `CURSOR_BRIDGE_CHAT_ONLY_WORKSPACE` | `true`
|
|
174
|
-
| `CURSOR_BRIDGE_VERBOSE`
|
|
175
|
-
| `CURSOR_BRIDGE_MAX_MODE`
|
|
176
|
-
| `
|
|
177
|
-
| `
|
|
178
|
-
| `
|
|
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
|
|
199
|
-
|
|
200
|
-
| `--tailscale`
|
|
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;
|
package/dist/cli/args.js
ADDED
|
@@ -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":"AAaA,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,WAAW,GAAG,EAAE,CAAC;IACrB,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAEpB,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,aAAa,EAAE,CAAC;YAC7C,KAAK,GAAG,IAAI,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxD,WAAW,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1B,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,gBAAgB,EAAE,CAAC;YACjD,MAAM,GAAG,IAAI,CAAC;YACd,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxD,WAAW,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1B,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,eAAe,EAAE,CAAC;YAClD,YAAY,GAAG,IAAI,CAAC;YACpB,SAAS;QACX,CAAC;QAED,IAAI,GAAG,KAAK,YAAY,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;YAC5C,SAAS,GAAG,IAAI,CAAC;YACjB,SAAS;QACX,CAAC;QAED,IAAI,GAAG,KAAK,cAAc,EAAE,CAAC;YAC3B,SAAS,GAAG,IAAI,CAAC;YACjB,SAAS;QACX,CAAC;QAED,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;YACxB,MAAM,GAAG,IAAI,CAAC;YACd,SAAS;QACX,CAAC;QAED,IAAI,GAAG,KAAK,aAAa,EAAE,CAAC;YAC1B,SAAS,GAAG,IAAI,CAAC;YACjB,SAAS;QACX,CAAC;QAED,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACrC,IAAI,GAAG,IAAI,CAAC;YACZ,SAAS;QACX,CAAC;QAED,IAAI,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/B,OAAO,GAAG,GAAG;iBACV,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC;iBACxB,KAAK,CAAC,GAAG,CAAC;iBACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;iBACpB,MAAM,CAAC,OAAO,CAAC,CAAC;YACnB,SAAS;QACX,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO;QACL,SAAS;QACT,IAAI;QACJ,KAAK;QACL,YAAY;QACZ,MAAM;QACN,WAAW;QACX,OAAO;QACP,SAAS;QACT,SAAS;QACT,MAAM;KACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,OAAO,CAAC,GAAG,CAAC,qBAAqB,OAAO,EAAE,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtB,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACzB,OAAO,CAAC,GAAG,CACT,gGAAgG,CACjG,CAAC;IACF,OAAO,CAAC,GAAG,CACT,sGAAsG,CACvG,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,CAAC,gEAAgE,CAAC,CAAC;IAC9E,OAAO,CAAC,GAAG,CACT,2EAA2E,CAC5E,CAAC;IACF,OAAO,CAAC,GAAG,CACT,mEAAmE,CACpE,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACxB,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;IACxE,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ACCOUNTS_DIR: string;
|
|
@@ -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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"}
|