cursor-bridge 1.2.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.
- package/dist/index.js +80 -16
- package/package.json +1 -1
- package/src/index.ts +129 -23
package/dist/index.js
CHANGED
|
@@ -5,12 +5,59 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
const ws_1 = __importDefault(require("ws"));
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
const path_1 = require("path");
|
|
8
11
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
12
|
const BRIDGE_URL = 'wss://cursor-226b2ae97542.herokuapp.com';
|
|
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
|
+
}
|
|
21
|
+
try {
|
|
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
|
+
};
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
|
|
52
|
+
chalk_1.default.gray('Failed to read Cursor auth. Is Cursor installed and logged in?'));
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
10
56
|
function generatePairCode() {
|
|
11
57
|
return Math.floor(100000 + Math.random() * 900000).toString();
|
|
12
58
|
}
|
|
13
59
|
const PAIR_CODE = generatePairCode();
|
|
60
|
+
const cursorAuth = getCursorAuthFromDB();
|
|
14
61
|
let ws = null;
|
|
15
62
|
let reconnectTimer = null;
|
|
16
63
|
function banner() {
|
|
@@ -28,22 +75,40 @@ function banner() {
|
|
|
28
75
|
chalk_1.default.bgHex('#A855F7').white.bold(` ${PAIR_CODE.split('').join(' ')} `));
|
|
29
76
|
console.log('');
|
|
30
77
|
console.log(chalk_1.default.gray(' ─────────────────────────────────────────'));
|
|
31
|
-
|
|
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
|
+
}
|
|
32
88
|
console.log(chalk_1.default.gray(' ─────────────────────────────────────────'));
|
|
33
89
|
console.log('');
|
|
34
|
-
console.log(chalk_1.default.gray(' Connecting to bridge server...'));
|
|
35
|
-
console.log('');
|
|
36
90
|
}
|
|
37
91
|
function connect() {
|
|
38
92
|
ws = new ws_1.default(BRIDGE_URL);
|
|
39
93
|
ws.on('open', () => {
|
|
40
94
|
console.log(chalk_1.default.hex('#22C55E')(' ✓ ') +
|
|
41
95
|
chalk_1.default.white('Connected to bridge server'));
|
|
96
|
+
// Pair as desktop client
|
|
42
97
|
ws.send(JSON.stringify({
|
|
43
98
|
type: 'pair',
|
|
44
99
|
payload: { pairCode: PAIR_CODE, clientType: 'desktop' },
|
|
45
100
|
timestamp: Date.now(),
|
|
46
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
|
+
}
|
|
47
112
|
console.log(chalk_1.default.gray(' Waiting for mobile device...'));
|
|
48
113
|
console.log('');
|
|
49
114
|
});
|
|
@@ -56,30 +121,29 @@ function connect() {
|
|
|
56
121
|
if (status === 'connected') {
|
|
57
122
|
console.log(chalk_1.default.hex('#22C55E')(' ✓ ') +
|
|
58
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('');
|
|
59
126
|
}
|
|
60
127
|
else if (status === 'waiting') {
|
|
61
|
-
|
|
128
|
+
// already shown
|
|
62
129
|
}
|
|
63
130
|
else if (status === 'disconnected') {
|
|
64
131
|
console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
|
|
65
132
|
chalk_1.default.gray('Mobile device disconnected'));
|
|
66
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
|
+
}
|
|
67
138
|
break;
|
|
68
139
|
}
|
|
69
140
|
case 'command': {
|
|
70
141
|
const prompt = message.payload.prompt;
|
|
71
142
|
console.log(chalk_1.default.hex('#A855F7')(' → ') +
|
|
72
|
-
chalk_1.default.white('
|
|
73
|
-
chalk_1.default.gray(prompt.substring(0, 80) +
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
type: 'response',
|
|
77
|
-
payload: {
|
|
78
|
-
status: 'received',
|
|
79
|
-
message: `Command received: ${prompt.substring(0, 50)}`,
|
|
80
|
-
},
|
|
81
|
-
timestamp: Date.now(),
|
|
82
|
-
}));
|
|
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
|
|
83
147
|
break;
|
|
84
148
|
}
|
|
85
149
|
case 'error': {
|
|
@@ -98,7 +162,7 @@ function connect() {
|
|
|
98
162
|
chalk_1.default.gray('Disconnected from bridge server'));
|
|
99
163
|
scheduleReconnect();
|
|
100
164
|
});
|
|
101
|
-
ws.on('error',
|
|
165
|
+
ws.on('error', err => {
|
|
102
166
|
console.error(chalk_1.default.hex('#EF4444')(' ✗ ') +
|
|
103
167
|
chalk_1.default.gray(`Connection error: ${err.message}`));
|
|
104
168
|
});
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,16 +1,91 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import WebSocket from 'ws';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import { existsSync } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
4
7
|
import chalk from 'chalk';
|
|
5
|
-
import os from 'os';
|
|
6
8
|
|
|
7
9
|
const BRIDGE_URL = 'wss://cursor-226b2ae97542.herokuapp.com';
|
|
8
10
|
|
|
11
|
+
interface CursorAuth {
|
|
12
|
+
accessToken: string;
|
|
13
|
+
refreshToken: string;
|
|
14
|
+
machineId: string;
|
|
15
|
+
macMachineId?: string;
|
|
16
|
+
}
|
|
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)) {
|
|
31
|
+
console.log(
|
|
32
|
+
chalk.hex('#EF4444')(' ✗ ') +
|
|
33
|
+
chalk.gray('Cursor database not found. Is Cursor installed?'),
|
|
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
|
+
};
|
|
74
|
+
} catch (err) {
|
|
75
|
+
console.log(
|
|
76
|
+
chalk.hex('#EF4444')(' ✗ ') +
|
|
77
|
+
chalk.gray('Failed to read Cursor auth. Is Cursor installed and logged in?'),
|
|
78
|
+
);
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
9
83
|
function generatePairCode(): string {
|
|
10
84
|
return Math.floor(100000 + Math.random() * 900000).toString();
|
|
11
85
|
}
|
|
12
86
|
|
|
13
87
|
const PAIR_CODE = generatePairCode();
|
|
88
|
+
const cursorAuth = getCursorAuthFromDB();
|
|
14
89
|
|
|
15
90
|
let ws: WebSocket | null = null;
|
|
16
91
|
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
@@ -42,15 +117,27 @@ function banner() {
|
|
|
42
117
|
console.log(
|
|
43
118
|
chalk.gray(' ─────────────────────────────────────────'),
|
|
44
119
|
);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
+
|
|
48
137
|
console.log(
|
|
49
138
|
chalk.gray(' ─────────────────────────────────────────'),
|
|
50
139
|
);
|
|
51
140
|
console.log('');
|
|
52
|
-
console.log(chalk.gray(' Connecting to bridge server...'));
|
|
53
|
-
console.log('');
|
|
54
141
|
}
|
|
55
142
|
|
|
56
143
|
function connect() {
|
|
@@ -62,6 +149,7 @@ function connect() {
|
|
|
62
149
|
chalk.white('Connected to bridge server'),
|
|
63
150
|
);
|
|
64
151
|
|
|
152
|
+
// Pair as desktop client
|
|
65
153
|
ws!.send(
|
|
66
154
|
JSON.stringify({
|
|
67
155
|
type: 'pair',
|
|
@@ -70,6 +158,21 @@ function connect() {
|
|
|
70
158
|
}),
|
|
71
159
|
);
|
|
72
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
|
+
|
|
73
176
|
console.log(chalk.gray(' Waiting for mobile device...'));
|
|
74
177
|
console.log('');
|
|
75
178
|
});
|
|
@@ -86,13 +189,22 @@ function connect() {
|
|
|
86
189
|
chalk.hex('#22C55E')(' ✓ ') +
|
|
87
190
|
chalk.white('Mobile device paired successfully!'),
|
|
88
191
|
);
|
|
192
|
+
console.log(
|
|
193
|
+
chalk.gray(' Ready to receive prompts from mobile.'),
|
|
194
|
+
);
|
|
195
|
+
console.log('');
|
|
89
196
|
} else if (status === 'waiting') {
|
|
90
|
-
|
|
197
|
+
// already shown
|
|
91
198
|
} else if (status === 'disconnected') {
|
|
92
199
|
console.log(
|
|
93
200
|
chalk.hex('#EF4444')(' ✗ ') +
|
|
94
201
|
chalk.gray('Mobile device disconnected'),
|
|
95
202
|
);
|
|
203
|
+
} else if (status === 'auth_stored') {
|
|
204
|
+
console.log(
|
|
205
|
+
chalk.hex('#22C55E')(' ✓ ') +
|
|
206
|
+
chalk.white('Auth credentials stored on bridge'),
|
|
207
|
+
);
|
|
96
208
|
}
|
|
97
209
|
break;
|
|
98
210
|
}
|
|
@@ -101,28 +213,22 @@ function connect() {
|
|
|
101
213
|
const prompt = message.payload.prompt as string;
|
|
102
214
|
console.log(
|
|
103
215
|
chalk.hex('#A855F7')(' → ') +
|
|
104
|
-
chalk.white('
|
|
105
|
-
chalk.gray(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
ws!.send(
|
|
110
|
-
JSON.stringify({
|
|
111
|
-
type: 'response',
|
|
112
|
-
payload: {
|
|
113
|
-
status: 'received',
|
|
114
|
-
message: `Command received: ${prompt.substring(0, 50)}`,
|
|
115
|
-
},
|
|
116
|
-
timestamp: Date.now(),
|
|
117
|
-
}),
|
|
216
|
+
chalk.white('Prompt: ') +
|
|
217
|
+
chalk.gray(
|
|
218
|
+
prompt.substring(0, 80) +
|
|
219
|
+
(prompt.length > 80 ? '...' : ''),
|
|
220
|
+
),
|
|
118
221
|
);
|
|
222
|
+
// Bridge handles API proxy now — no need to do anything locally
|
|
119
223
|
break;
|
|
120
224
|
}
|
|
121
225
|
|
|
122
226
|
case 'error': {
|
|
123
227
|
console.log(
|
|
124
228
|
chalk.hex('#EF4444')(' ✗ ') +
|
|
125
|
-
chalk.gray(
|
|
229
|
+
chalk.gray(
|
|
230
|
+
(message.payload.message as string) || 'Unknown error',
|
|
231
|
+
),
|
|
126
232
|
);
|
|
127
233
|
break;
|
|
128
234
|
}
|
|
@@ -140,7 +246,7 @@ function connect() {
|
|
|
140
246
|
scheduleReconnect();
|
|
141
247
|
});
|
|
142
248
|
|
|
143
|
-
ws.on('error',
|
|
249
|
+
ws.on('error', err => {
|
|
144
250
|
console.error(
|
|
145
251
|
chalk.hex('#EF4444')(' ✗ ') +
|
|
146
252
|
chalk.gray(`Connection error: ${err.message}`),
|