cursor-bridge 1.3.0 → 1.4.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 (3) hide show
  1. package/dist/index.js +73 -46
  2. package/package.json +1 -1
  3. package/src/index.ts +118 -56
package/dist/index.js CHANGED
@@ -6,48 +6,58 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const ws_1 = __importDefault(require("ws"));
8
8
  const child_process_1 = require("child_process");
9
+ const fs_1 = require("fs");
10
+ const path_1 = require("path");
9
11
  const chalk_1 = __importDefault(require("chalk"));
10
12
  const BRIDGE_URL = 'wss://cursor-226b2ae97542.herokuapp.com';
11
- function sendToCursor(prompt) {
13
+ function getCursorAuthFromDB() {
14
+ const home = process.env.HOME || process.env.USERPROFILE || '';
15
+ const dbPath = (0, path_1.join)(home, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'state.vscdb');
16
+ if (!(0, fs_1.existsSync)(dbPath)) {
17
+ console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
18
+ chalk_1.default.gray('Cursor database not found. Is Cursor installed?'));
19
+ return null;
20
+ }
12
21
  try {
13
- // Escape special characters for AppleScript
14
- const escaped = prompt
15
- .replace(/\\/g, '\\\\')
16
- .replace(/"/g, '\\"')
17
- .replace(/\n/g, '\\n');
18
- const script = `
19
- tell application "Cursor"
20
- activate
21
- end tell
22
- delay 0.5
23
- tell application "System Events"
24
- tell process "Cursor"
25
- -- Open Composer with Cmd+I
26
- keystroke "i" using command down
27
- delay 0.3
28
- -- Type the prompt
29
- keystroke "${escaped}"
30
- delay 0.2
31
- -- Submit with Enter
32
- key code 36
33
- end tell
34
- end tell
35
- `;
36
- (0, child_process_1.execSync)(`osascript -e '${script.replace(/'/g, "'\\''")}'`, {
37
- timeout: 10000,
38
- });
39
- console.log(chalk_1.default.hex('#22C55E')(' ✓ ') +
40
- chalk_1.default.white('Sent to Cursor IDE'));
22
+ // Read auth tokens from Cursor's SQLite database
23
+ const query = (key) => {
24
+ const result = (0, child_process_1.execSync)(`sqlite3 "${dbPath}" "SELECT value FROM ItemTable WHERE key='${key}'" 2>/dev/null`, { encoding: 'utf-8', timeout: 5000 }).trim();
25
+ return result;
26
+ };
27
+ const accessToken = query('cursorAuth/accessToken');
28
+ const refreshToken = query('cursorAuth/refreshToken');
29
+ const machineId = query('storage.serviceMachineId');
30
+ if (!accessToken || !refreshToken) {
31
+ console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
32
+ chalk_1.default.gray('Not logged into Cursor. Please log in first.'));
33
+ return null;
34
+ }
35
+ // macMachineId is optional
36
+ let macMachineId;
37
+ try {
38
+ macMachineId = query('storage.macMachineId') || undefined;
39
+ }
40
+ catch {
41
+ // not available on all platforms
42
+ }
43
+ return {
44
+ accessToken: accessToken.replace(/"/g, ''),
45
+ refreshToken: refreshToken.replace(/"/g, ''),
46
+ machineId: machineId.replace(/"/g, ''),
47
+ macMachineId: macMachineId?.replace(/"/g, ''),
48
+ };
41
49
  }
42
50
  catch (err) {
43
51
  console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
44
- chalk_1.default.gray('Failed to send to Cursor. Is it running?'));
52
+ chalk_1.default.gray('Failed to read Cursor auth. Is Cursor installed and logged in?'));
53
+ return null;
45
54
  }
46
55
  }
47
56
  function generatePairCode() {
48
57
  return Math.floor(100000 + Math.random() * 900000).toString();
49
58
  }
50
59
  const PAIR_CODE = generatePairCode();
60
+ const cursorAuth = getCursorAuthFromDB();
51
61
  let ws = null;
52
62
  let reconnectTimer = null;
53
63
  function banner() {
@@ -65,22 +75,40 @@ function banner() {
65
75
  chalk_1.default.bgHex('#A855F7').white.bold(` ${PAIR_CODE.split('').join(' ')} `));
66
76
  console.log('');
67
77
  console.log(chalk_1.default.gray(' ─────────────────────────────────────────'));
68
- console.log(chalk_1.default.gray(' Bridge: ') + chalk_1.default.white(BRIDGE_URL));
78
+ if (cursorAuth) {
79
+ console.log(chalk_1.default.hex('#22C55E')(' ✓ ') +
80
+ chalk_1.default.white('Cursor auth detected'));
81
+ console.log(chalk_1.default.gray(' Mode: ') +
82
+ chalk_1.default.white('Direct API (works without Cursor open)'));
83
+ }
84
+ else {
85
+ console.log(chalk_1.default.hex('#F59E0B')(' ⚠ ') +
86
+ chalk_1.default.gray('No Cursor auth found — limited functionality'));
87
+ }
69
88
  console.log(chalk_1.default.gray(' ─────────────────────────────────────────'));
70
89
  console.log('');
71
- console.log(chalk_1.default.gray(' Connecting to bridge server...'));
72
- console.log('');
73
90
  }
74
91
  function connect() {
75
92
  ws = new ws_1.default(BRIDGE_URL);
76
93
  ws.on('open', () => {
77
94
  console.log(chalk_1.default.hex('#22C55E')(' ✓ ') +
78
95
  chalk_1.default.white('Connected to bridge server'));
96
+ // Pair as desktop client
79
97
  ws.send(JSON.stringify({
80
98
  type: 'pair',
81
99
  payload: { pairCode: PAIR_CODE, clientType: 'desktop' },
82
100
  timestamp: Date.now(),
83
101
  }));
102
+ // Send Cursor auth to bridge so it can proxy API calls
103
+ if (cursorAuth) {
104
+ ws.send(JSON.stringify({
105
+ type: 'auth',
106
+ payload: cursorAuth,
107
+ timestamp: Date.now(),
108
+ }));
109
+ console.log(chalk_1.default.hex('#22C55E')(' ✓ ') +
110
+ chalk_1.default.white('Cursor auth sent to bridge'));
111
+ }
84
112
  console.log(chalk_1.default.gray(' Waiting for mobile device...'));
85
113
  console.log('');
86
114
  });
@@ -93,30 +121,29 @@ function connect() {
93
121
  if (status === 'connected') {
94
122
  console.log(chalk_1.default.hex('#22C55E')(' ✓ ') +
95
123
  chalk_1.default.white('Mobile device paired successfully!'));
124
+ console.log(chalk_1.default.gray(' Ready to receive prompts from mobile.'));
125
+ console.log('');
96
126
  }
97
127
  else if (status === 'waiting') {
98
- console.log(chalk_1.default.gray(' Waiting for mobile device...'));
128
+ // already shown
99
129
  }
100
130
  else if (status === 'disconnected') {
101
131
  console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
102
132
  chalk_1.default.gray('Mobile device disconnected'));
103
133
  }
134
+ else if (status === 'auth_stored') {
135
+ console.log(chalk_1.default.hex('#22C55E')(' ✓ ') +
136
+ chalk_1.default.white('Auth credentials stored on bridge'));
137
+ }
104
138
  break;
105
139
  }
106
140
  case 'command': {
107
141
  const prompt = message.payload.prompt;
108
142
  console.log(chalk_1.default.hex('#A855F7')(' → ') +
109
- chalk_1.default.white('Command received: ') +
110
- chalk_1.default.gray(prompt.substring(0, 80) + (prompt.length > 80 ? '...' : '')));
111
- sendToCursor(prompt);
112
- ws.send(JSON.stringify({
113
- type: 'response',
114
- payload: {
115
- status: 'sent',
116
- message: 'Sent to Cursor IDE',
117
- },
118
- timestamp: Date.now(),
119
- }));
143
+ chalk_1.default.white('Prompt: ') +
144
+ chalk_1.default.gray(prompt.substring(0, 80) +
145
+ (prompt.length > 80 ? '...' : '')));
146
+ // Bridge handles API proxy now — no need to do anything locally
120
147
  break;
121
148
  }
122
149
  case 'error': {
@@ -135,7 +162,7 @@ function connect() {
135
162
  chalk_1.default.gray('Disconnected from bridge server'));
136
163
  scheduleReconnect();
137
164
  });
138
- ws.on('error', (err) => {
165
+ ws.on('error', err => {
139
166
  console.error(chalk_1.default.hex('#EF4444')(' ✗ ') +
140
167
  chalk_1.default.gray(`Connection error: ${err.message}`));
141
168
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursor-bridge",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "CLI bridge to connect IDE For Cursor mobile app with desktop Cursor IDE",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -2,50 +2,81 @@
2
2
 
3
3
  import WebSocket from 'ws';
4
4
  import { execSync } from 'child_process';
5
+ import { existsSync } from 'fs';
6
+ import { join } from 'path';
5
7
  import chalk from 'chalk';
6
8
 
7
9
  const BRIDGE_URL = 'wss://cursor-226b2ae97542.herokuapp.com';
8
10
 
9
- function sendToCursor(prompt: string): void {
10
- try {
11
- // Escape special characters for AppleScript
12
- const escaped = prompt
13
- .replace(/\\/g, '\\\\')
14
- .replace(/"/g, '\\"')
15
- .replace(/\n/g, '\\n');
16
-
17
- const script = `
18
- tell application "Cursor"
19
- activate
20
- end tell
21
- delay 0.5
22
- tell application "System Events"
23
- tell process "Cursor"
24
- -- Open Composer with Cmd+I
25
- keystroke "i" using command down
26
- delay 0.3
27
- -- Type the prompt
28
- keystroke "${escaped}"
29
- delay 0.2
30
- -- Submit with Enter
31
- key code 36
32
- end tell
33
- end tell
34
- `;
35
-
36
- execSync(`osascript -e '${script.replace(/'/g, "'\\''")}'`, {
37
- timeout: 10000,
38
- });
11
+ interface CursorAuth {
12
+ accessToken: string;
13
+ refreshToken: string;
14
+ machineId: string;
15
+ macMachineId?: string;
16
+ }
39
17
 
18
+ function getCursorAuthFromDB(): CursorAuth | null {
19
+ const home = process.env.HOME || process.env.USERPROFILE || '';
20
+ const dbPath = join(
21
+ home,
22
+ 'Library',
23
+ 'Application Support',
24
+ 'Cursor',
25
+ 'User',
26
+ 'globalStorage',
27
+ 'state.vscdb',
28
+ );
29
+
30
+ if (!existsSync(dbPath)) {
40
31
  console.log(
41
- chalk.hex('#22C55E')(' ') +
42
- chalk.white('Sent to Cursor IDE'),
32
+ chalk.hex('#EF4444')(' ') +
33
+ chalk.gray('Cursor database not found. Is Cursor installed?'),
43
34
  );
35
+ return null;
36
+ }
37
+
38
+ try {
39
+ // Read auth tokens from Cursor's SQLite database
40
+ const query = (key: string): string => {
41
+ const result = execSync(
42
+ `sqlite3 "${dbPath}" "SELECT value FROM ItemTable WHERE key='${key}'" 2>/dev/null`,
43
+ { encoding: 'utf-8', timeout: 5000 },
44
+ ).trim();
45
+ return result;
46
+ };
47
+
48
+ const accessToken = query('cursorAuth/accessToken');
49
+ const refreshToken = query('cursorAuth/refreshToken');
50
+ const machineId = query('storage.serviceMachineId');
51
+
52
+ if (!accessToken || !refreshToken) {
53
+ console.log(
54
+ chalk.hex('#EF4444')(' ✗ ') +
55
+ chalk.gray('Not logged into Cursor. Please log in first.'),
56
+ );
57
+ return null;
58
+ }
59
+
60
+ // macMachineId is optional
61
+ let macMachineId: string | undefined;
62
+ try {
63
+ macMachineId = query('storage.macMachineId') || undefined;
64
+ } catch {
65
+ // not available on all platforms
66
+ }
67
+
68
+ return {
69
+ accessToken: accessToken.replace(/"/g, ''),
70
+ refreshToken: refreshToken.replace(/"/g, ''),
71
+ machineId: machineId.replace(/"/g, ''),
72
+ macMachineId: macMachineId?.replace(/"/g, ''),
73
+ };
44
74
  } catch (err) {
45
75
  console.log(
46
76
  chalk.hex('#EF4444')(' ✗ ') +
47
- chalk.gray('Failed to send to Cursor. Is it running?'),
77
+ chalk.gray('Failed to read Cursor auth. Is Cursor installed and logged in?'),
48
78
  );
79
+ return null;
49
80
  }
50
81
  }
51
82
 
@@ -54,6 +85,7 @@ function generatePairCode(): string {
54
85
  }
55
86
 
56
87
  const PAIR_CODE = generatePairCode();
88
+ const cursorAuth = getCursorAuthFromDB();
57
89
 
58
90
  let ws: WebSocket | null = null;
59
91
  let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
@@ -85,15 +117,27 @@ function banner() {
85
117
  console.log(
86
118
  chalk.gray(' ─────────────────────────────────────────'),
87
119
  );
88
- console.log(
89
- chalk.gray(' Bridge: ') + chalk.white(BRIDGE_URL),
90
- );
120
+
121
+ if (cursorAuth) {
122
+ console.log(
123
+ chalk.hex('#22C55E')(' ✓ ') +
124
+ chalk.white('Cursor auth detected'),
125
+ );
126
+ console.log(
127
+ chalk.gray(' Mode: ') +
128
+ chalk.white('Direct API (works without Cursor open)'),
129
+ );
130
+ } else {
131
+ console.log(
132
+ chalk.hex('#F59E0B')(' ⚠ ') +
133
+ chalk.gray('No Cursor auth found — limited functionality'),
134
+ );
135
+ }
136
+
91
137
  console.log(
92
138
  chalk.gray(' ─────────────────────────────────────────'),
93
139
  );
94
140
  console.log('');
95
- console.log(chalk.gray(' Connecting to bridge server...'));
96
- console.log('');
97
141
  }
98
142
 
99
143
  function connect() {
@@ -105,6 +149,7 @@ function connect() {
105
149
  chalk.white('Connected to bridge server'),
106
150
  );
107
151
 
152
+ // Pair as desktop client
108
153
  ws!.send(
109
154
  JSON.stringify({
110
155
  type: 'pair',
@@ -113,6 +158,21 @@ function connect() {
113
158
  }),
114
159
  );
115
160
 
161
+ // Send Cursor auth to bridge so it can proxy API calls
162
+ if (cursorAuth) {
163
+ ws!.send(
164
+ JSON.stringify({
165
+ type: 'auth',
166
+ payload: cursorAuth,
167
+ timestamp: Date.now(),
168
+ }),
169
+ );
170
+ console.log(
171
+ chalk.hex('#22C55E')(' ✓ ') +
172
+ chalk.white('Cursor auth sent to bridge'),
173
+ );
174
+ }
175
+
116
176
  console.log(chalk.gray(' Waiting for mobile device...'));
117
177
  console.log('');
118
178
  });
@@ -129,13 +189,22 @@ function connect() {
129
189
  chalk.hex('#22C55E')(' ✓ ') +
130
190
  chalk.white('Mobile device paired successfully!'),
131
191
  );
192
+ console.log(
193
+ chalk.gray(' Ready to receive prompts from mobile.'),
194
+ );
195
+ console.log('');
132
196
  } else if (status === 'waiting') {
133
- console.log(chalk.gray(' Waiting for mobile device...'));
197
+ // already shown
134
198
  } else if (status === 'disconnected') {
135
199
  console.log(
136
200
  chalk.hex('#EF4444')(' ✗ ') +
137
201
  chalk.gray('Mobile device disconnected'),
138
202
  );
203
+ } else if (status === 'auth_stored') {
204
+ console.log(
205
+ chalk.hex('#22C55E')(' ✓ ') +
206
+ chalk.white('Auth credentials stored on bridge'),
207
+ );
139
208
  }
140
209
  break;
141
210
  }
@@ -144,29 +213,22 @@ function connect() {
144
213
  const prompt = message.payload.prompt as string;
145
214
  console.log(
146
215
  chalk.hex('#A855F7')(' → ') +
147
- chalk.white('Command received: ') +
148
- chalk.gray(prompt.substring(0, 80) + (prompt.length > 80 ? '...' : '')),
149
- );
150
-
151
- sendToCursor(prompt);
152
-
153
- ws!.send(
154
- JSON.stringify({
155
- type: 'response',
156
- payload: {
157
- status: 'sent',
158
- message: 'Sent to Cursor IDE',
159
- },
160
- timestamp: Date.now(),
161
- }),
216
+ chalk.white('Prompt: ') +
217
+ chalk.gray(
218
+ prompt.substring(0, 80) +
219
+ (prompt.length > 80 ? '...' : ''),
220
+ ),
162
221
  );
222
+ // Bridge handles API proxy now — no need to do anything locally
163
223
  break;
164
224
  }
165
225
 
166
226
  case 'error': {
167
227
  console.log(
168
228
  chalk.hex('#EF4444')(' ✗ ') +
169
- chalk.gray(message.payload.message || 'Unknown error'),
229
+ chalk.gray(
230
+ (message.payload.message as string) || 'Unknown error',
231
+ ),
170
232
  );
171
233
  break;
172
234
  }
@@ -184,7 +246,7 @@ function connect() {
184
246
  scheduleReconnect();
185
247
  });
186
248
 
187
- ws.on('error', (err) => {
249
+ ws.on('error', err => {
188
250
  console.error(
189
251
  chalk.hex('#EF4444')(' ✗ ') +
190
252
  chalk.gray(`Connection error: ${err.message}`),