cursor-bridge 1.1.0 → 1.2.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 +64 -140
  2. package/package.json +2 -4
  3. package/src/index.ts +78 -194
package/dist/index.js CHANGED
@@ -4,44 +4,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- const ws_1 = require("ws");
8
- const uuid_1 = require("uuid");
7
+ const ws_1 = __importDefault(require("ws"));
9
8
  const chalk_1 = __importDefault(require("chalk"));
10
- const os_1 = __importDefault(require("os"));
11
- const PORT = Number(process.argv[2]) || 9090;
12
- const TOKEN = (0, uuid_1.v4)();
13
- // 6 haneli pair code üret
9
+ const BRIDGE_URL = 'wss://cursor-226b2ae97542.herokuapp.com';
14
10
  function generatePairCode() {
15
11
  return Math.floor(100000 + Math.random() * 900000).toString();
16
12
  }
17
13
  const PAIR_CODE = generatePairCode();
18
- const clients = new Map();
19
- function getLocalIP() {
20
- const interfaces = os_1.default.networkInterfaces();
21
- for (const name of Object.keys(interfaces)) {
22
- for (const iface of interfaces[name] || []) {
23
- if (iface.family === 'IPv4' && !iface.internal) {
24
- return iface.address;
25
- }
26
- }
27
- }
28
- return 'localhost';
29
- }
30
- function findPeer(clientId) {
31
- const client = clients.get(clientId);
32
- if (!client)
33
- return undefined;
34
- for (const [, other] of clients) {
35
- if (other.id !== clientId &&
36
- other.token === client.token &&
37
- other.type !== client.type) {
38
- return other;
39
- }
40
- }
41
- return undefined;
42
- }
14
+ let ws = null;
15
+ let reconnectTimer = null;
43
16
  function banner() {
44
- const ip = getLocalIP();
45
17
  console.clear();
46
18
  console.log('');
47
19
  console.log(chalk_1.default.hex('#A855F7').bold(' ╔══════════════════════════════════════╗'));
@@ -56,145 +28,97 @@ function banner() {
56
28
  chalk_1.default.bgHex('#A855F7').white.bold(` ${PAIR_CODE.split('').join(' ')} `));
57
29
  console.log('');
58
30
  console.log(chalk_1.default.gray(' ─────────────────────────────────────────'));
59
- console.log(chalk_1.default.gray(' Host: ') + chalk_1.default.white(ip));
60
- console.log(chalk_1.default.gray(' Port: ') + chalk_1.default.white(String(PORT)));
31
+ console.log(chalk_1.default.gray(' Bridge: ') + chalk_1.default.white(BRIDGE_URL));
61
32
  console.log(chalk_1.default.gray(' ─────────────────────────────────────────'));
62
33
  console.log('');
63
- console.log(chalk_1.default.gray(' Waiting for mobile connection...'));
34
+ console.log(chalk_1.default.gray(' Connecting to bridge server...'));
64
35
  console.log('');
65
36
  }
66
- const wss = new ws_1.WebSocketServer({ port: PORT });
67
- wss.on('error', error => {
68
- console.error(chalk_1.default.red(' WebSocket error:'), error.message);
69
- });
70
- wss.on('connection', (ws) => {
71
- const clientId = (0, uuid_1.v4)();
72
- ws.on('error', error => {
73
- console.error(chalk_1.default.red(` Client error:`), error.message);
74
- clients.delete(clientId);
37
+ function connect() {
38
+ ws = new ws_1.default(BRIDGE_URL);
39
+ ws.on('open', () => {
40
+ console.log(chalk_1.default.hex('#22C55E')(' ✓ ') +
41
+ chalk_1.default.white('Connected to bridge server'));
42
+ ws.send(JSON.stringify({
43
+ type: 'pair',
44
+ payload: { pairCode: PAIR_CODE, clientType: 'desktop' },
45
+ timestamp: Date.now(),
46
+ }));
47
+ console.log(chalk_1.default.gray(' Waiting for mobile device...'));
48
+ console.log('');
75
49
  });
76
50
  ws.on('message', (raw) => {
77
51
  try {
78
52
  const message = JSON.parse(raw.toString());
79
53
  switch (message.type) {
80
- case 'pair': {
81
- const { pairCode, clientType } = message.payload;
82
- // Pair code doğrula
83
- if (pairCode !== PAIR_CODE) {
84
- ws.send(JSON.stringify({
85
- type: 'error',
86
- payload: { message: 'Invalid pair code' },
87
- timestamp: Date.now(),
88
- }));
89
- console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
90
- chalk_1.default.gray(`Invalid pair code attempt: ${pairCode}`));
91
- return;
92
- }
93
- clients.set(clientId, {
94
- id: clientId,
95
- ws,
96
- type: clientType,
97
- token: TOKEN,
98
- });
99
- const peer = findPeer(clientId);
100
- ws.send(JSON.stringify({
101
- type: 'status',
102
- payload: {
103
- status: peer ? 'connected' : 'waiting',
104
- clientId,
105
- host: getLocalIP(),
106
- port: PORT,
107
- },
108
- timestamp: Date.now(),
109
- }));
110
- if (peer) {
111
- peer.ws.send(JSON.stringify({
112
- type: 'status',
113
- payload: { status: 'connected' },
114
- timestamp: Date.now(),
115
- }));
116
- console.log(chalk_1.default.hex('#22C55E')(' ✓ ') +
117
- chalk_1.default.white(`${clientType} paired successfully!`));
118
- }
119
- else {
54
+ case 'status': {
55
+ const status = message.payload.status;
56
+ if (status === 'connected') {
120
57
  console.log(chalk_1.default.hex('#22C55E')(' ✓ ') +
121
- chalk_1.default.white(`${clientType} connected with pair code`));
58
+ chalk_1.default.white('Mobile device paired successfully!'));
122
59
  }
123
- break;
124
- }
125
- case 'command': {
126
- const peer = findPeer(clientId);
127
- if (peer && peer.ws.readyState === ws_1.WebSocket.OPEN) {
128
- peer.ws.send(JSON.stringify({
129
- ...message,
130
- from: clientId,
131
- timestamp: Date.now(),
132
- }));
133
- console.log(chalk_1.default.hex('#A855F7')(' → ') +
134
- chalk_1.default.gray('Command forwarded to desktop'));
60
+ else if (status === 'waiting') {
61
+ console.log(chalk_1.default.gray(' Waiting for mobile device...'));
135
62
  }
136
- break;
137
- }
138
- case 'response': {
139
- const peer = findPeer(clientId);
140
- if (peer && peer.ws.readyState === ws_1.WebSocket.OPEN) {
141
- peer.ws.send(JSON.stringify({
142
- ...message,
143
- from: clientId,
144
- timestamp: Date.now(),
145
- }));
146
- console.log(chalk_1.default.hex('#A855F7')(' ← ') +
147
- chalk_1.default.gray('Response forwarded to mobile'));
63
+ else if (status === 'disconnected') {
64
+ console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
65
+ chalk_1.default.gray('Mobile device disconnected'));
148
66
  }
149
67
  break;
150
68
  }
151
- case 'ping': {
69
+ case 'command': {
70
+ const prompt = message.payload.prompt;
71
+ console.log(chalk_1.default.hex('#A855F7')(' → ') +
72
+ chalk_1.default.white('Command received: ') +
73
+ chalk_1.default.gray(prompt.substring(0, 80) + (prompt.length > 80 ? '...' : '')));
74
+ // Send response back
152
75
  ws.send(JSON.stringify({
153
- type: 'pong',
154
- payload: {},
76
+ type: 'response',
77
+ payload: {
78
+ status: 'received',
79
+ message: `Command received: ${prompt.substring(0, 50)}`,
80
+ },
155
81
  timestamp: Date.now(),
156
82
  }));
157
83
  break;
158
84
  }
85
+ case 'error': {
86
+ console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
87
+ chalk_1.default.gray(message.payload.message || 'Unknown error'));
88
+ break;
89
+ }
159
90
  }
160
91
  }
161
92
  catch {
162
- ws.send(JSON.stringify({
163
- type: 'error',
164
- payload: { message: 'Invalid message format' },
165
- }));
93
+ console.warn(chalk_1.default.gray(' Invalid message received'));
166
94
  }
167
95
  });
168
96
  ws.on('close', () => {
169
- const client = clients.get(clientId);
170
- if (client) {
171
- const peer = findPeer(clientId);
172
- if (peer && peer.ws.readyState === ws_1.WebSocket.OPEN) {
173
- peer.ws.send(JSON.stringify({
174
- type: 'status',
175
- payload: { status: 'disconnected' },
176
- timestamp: Date.now(),
177
- }));
178
- }
179
- console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
180
- chalk_1.default.gray(`${client.type} device disconnected`));
181
- clients.delete(clientId);
182
- }
97
+ console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
98
+ chalk_1.default.gray('Disconnected from bridge server'));
99
+ scheduleReconnect();
183
100
  });
184
- });
185
- // Heartbeat
186
- const heartbeat = setInterval(() => {
187
- clients.forEach(client => {
188
- if (client.ws.readyState === ws_1.WebSocket.OPEN) {
189
- client.ws.ping();
190
- }
101
+ ws.on('error', (err) => {
102
+ console.error(chalk_1.default.hex('#EF4444')(' ✗ ') +
103
+ chalk_1.default.gray(`Connection error: ${err.message}`));
191
104
  });
192
- }, 30000);
105
+ }
106
+ function scheduleReconnect() {
107
+ if (reconnectTimer)
108
+ return;
109
+ console.log(chalk_1.default.gray(' Reconnecting in 3s...'));
110
+ reconnectTimer = setTimeout(() => {
111
+ reconnectTimer = null;
112
+ connect();
113
+ }, 3000);
114
+ }
193
115
  banner();
116
+ connect();
194
117
  process.on('SIGINT', () => {
195
118
  console.log('');
196
119
  console.log(chalk_1.default.gray(' Bridge stopped.'));
197
- clearInterval(heartbeat);
198
- wss.close();
120
+ if (reconnectTimer)
121
+ clearTimeout(reconnectTimer);
122
+ ws?.close();
199
123
  process.exit(0);
200
124
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursor-bridge",
3
- "version": "1.1.0",
3
+ "version": "1.2.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": {
@@ -16,9 +16,7 @@
16
16
  "license": "MIT",
17
17
  "dependencies": {
18
18
  "chalk": "^4.1.2",
19
- "ws": "^8.20.0",
20
- "uuid": "^13.0.0",
21
- "qrcode-terminal": "^0.12.0"
19
+ "ws": "^8.20.0"
22
20
  },
23
21
  "devDependencies": {
24
22
  "@types/ws": "^8.18.1",
package/src/index.ts CHANGED
@@ -1,67 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { WebSocketServer, WebSocket } from 'ws';
4
- import { v4 as uuidv4 } from 'uuid';
3
+ import WebSocket from 'ws';
5
4
  import chalk from 'chalk';
6
5
  import os from 'os';
7
6
 
8
- const PORT = Number(process.argv[2]) || 9090;
9
- const TOKEN = uuidv4();
7
+ const BRIDGE_URL = 'wss://cursor-226b2ae97542.herokuapp.com';
10
8
 
11
- // 6 haneli pair code üret
12
9
  function generatePairCode(): string {
13
10
  return Math.floor(100000 + Math.random() * 900000).toString();
14
11
  }
15
12
 
16
13
  const PAIR_CODE = generatePairCode();
17
14
 
18
- interface BridgeMessage {
19
- type: string;
20
- payload: Record<string, unknown>;
21
- from?: string;
22
- timestamp: number;
23
- }
24
-
25
- interface ConnectedClient {
26
- id: string;
27
- ws: WebSocket;
28
- type: 'mobile' | 'desktop';
29
- token: string;
30
- }
31
-
32
- const clients = new Map<string, ConnectedClient>();
33
-
34
- function getLocalIP(): string {
35
- const interfaces = os.networkInterfaces();
36
- for (const name of Object.keys(interfaces)) {
37
- for (const iface of interfaces[name] || []) {
38
- if (iface.family === 'IPv4' && !iface.internal) {
39
- return iface.address;
40
- }
41
- }
42
- }
43
- return 'localhost';
44
- }
45
-
46
- function findPeer(clientId: string): ConnectedClient | undefined {
47
- const client = clients.get(clientId);
48
- if (!client) return undefined;
49
-
50
- for (const [, other] of clients) {
51
- if (
52
- other.id !== clientId &&
53
- other.token === client.token &&
54
- other.type !== client.type
55
- ) {
56
- return other;
57
- }
58
- }
59
- return undefined;
60
- }
15
+ let ws: WebSocket | null = null;
16
+ let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
61
17
 
62
18
  function banner() {
63
- const ip = getLocalIP();
64
-
65
19
  console.clear();
66
20
  console.log('');
67
21
  console.log(
@@ -89,197 +43,127 @@ function banner() {
89
43
  chalk.gray(' ─────────────────────────────────────────'),
90
44
  );
91
45
  console.log(
92
- chalk.gray(' Host: ') + chalk.white(ip),
93
- );
94
- console.log(
95
- chalk.gray(' Port: ') + chalk.white(String(PORT)),
46
+ chalk.gray(' Bridge: ') + chalk.white(BRIDGE_URL),
96
47
  );
97
48
  console.log(
98
49
  chalk.gray(' ─────────────────────────────────────────'),
99
50
  );
100
51
  console.log('');
101
- console.log(chalk.gray(' Waiting for mobile connection...'));
52
+ console.log(chalk.gray(' Connecting to bridge server...'));
102
53
  console.log('');
103
54
  }
104
55
 
105
- const wss = new WebSocketServer({ port: PORT });
106
-
107
- wss.on('error', error => {
108
- console.error(chalk.red(' WebSocket error:'), error.message);
109
- });
110
-
111
- wss.on('connection', (ws: WebSocket) => {
112
- const clientId = uuidv4();
113
-
114
- ws.on('error', error => {
115
- console.error(chalk.red(` Client error:`), error.message);
116
- clients.delete(clientId);
56
+ function connect() {
57
+ ws = new WebSocket(BRIDGE_URL);
58
+
59
+ ws.on('open', () => {
60
+ console.log(
61
+ chalk.hex('#22C55E')(' ✓ ') +
62
+ chalk.white('Connected to bridge server'),
63
+ );
64
+
65
+ ws!.send(
66
+ JSON.stringify({
67
+ type: 'pair',
68
+ payload: { pairCode: PAIR_CODE, clientType: 'desktop' },
69
+ timestamp: Date.now(),
70
+ }),
71
+ );
72
+
73
+ console.log(chalk.gray(' Waiting for mobile device...'));
74
+ console.log('');
117
75
  });
118
76
 
119
77
  ws.on('message', (raw: Buffer) => {
120
78
  try {
121
- const message: BridgeMessage = JSON.parse(raw.toString());
79
+ const message = JSON.parse(raw.toString());
122
80
 
123
81
  switch (message.type) {
124
- case 'pair': {
125
- const { pairCode, clientType } = message.payload as {
126
- pairCode: string;
127
- clientType: 'mobile' | 'desktop';
128
- };
129
-
130
- // Pair code doğrula
131
- if (pairCode !== PAIR_CODE) {
132
- ws.send(
133
- JSON.stringify({
134
- type: 'error',
135
- payload: { message: 'Invalid pair code' },
136
- timestamp: Date.now(),
137
- }),
82
+ case 'status': {
83
+ const status = message.payload.status as string;
84
+ if (status === 'connected') {
85
+ console.log(
86
+ chalk.hex('#22C55E')(' ✓ ') +
87
+ chalk.white('Mobile device paired successfully!'),
138
88
  );
89
+ } else if (status === 'waiting') {
90
+ console.log(chalk.gray(' Waiting for mobile device...'));
91
+ } else if (status === 'disconnected') {
139
92
  console.log(
140
93
  chalk.hex('#EF4444')(' ✗ ') +
141
- chalk.gray(`Invalid pair code attempt: ${pairCode}`),
94
+ chalk.gray('Mobile device disconnected'),
142
95
  );
143
- return;
144
96
  }
97
+ break;
98
+ }
145
99
 
146
- clients.set(clientId, {
147
- id: clientId,
148
- ws,
149
- type: clientType,
150
- token: TOKEN,
151
- });
152
-
153
- const peer = findPeer(clientId);
100
+ case 'command': {
101
+ const prompt = message.payload.prompt as string;
102
+ console.log(
103
+ chalk.hex('#A855F7')(' → ') +
104
+ chalk.white('Command received: ') +
105
+ chalk.gray(prompt.substring(0, 80) + (prompt.length > 80 ? '...' : '')),
106
+ );
154
107
 
155
- ws.send(
108
+ // Send response back
109
+ ws!.send(
156
110
  JSON.stringify({
157
- type: 'status',
111
+ type: 'response',
158
112
  payload: {
159
- status: peer ? 'connected' : 'waiting',
160
- clientId,
161
- host: getLocalIP(),
162
- port: PORT,
113
+ status: 'received',
114
+ message: `Command received: ${prompt.substring(0, 50)}`,
163
115
  },
164
116
  timestamp: Date.now(),
165
117
  }),
166
118
  );
167
-
168
- if (peer) {
169
- peer.ws.send(
170
- JSON.stringify({
171
- type: 'status',
172
- payload: { status: 'connected' },
173
- timestamp: Date.now(),
174
- }),
175
- );
176
- console.log(
177
- chalk.hex('#22C55E')(' ✓ ') +
178
- chalk.white(`${clientType} paired successfully!`),
179
- );
180
- } else {
181
- console.log(
182
- chalk.hex('#22C55E')(' ✓ ') +
183
- chalk.white(`${clientType} connected with pair code`),
184
- );
185
- }
186
- break;
187
- }
188
-
189
- case 'command': {
190
- const peer = findPeer(clientId);
191
- if (peer && peer.ws.readyState === WebSocket.OPEN) {
192
- peer.ws.send(
193
- JSON.stringify({
194
- ...message,
195
- from: clientId,
196
- timestamp: Date.now(),
197
- }),
198
- );
199
- console.log(
200
- chalk.hex('#A855F7')(' → ') +
201
- chalk.gray('Command forwarded to desktop'),
202
- );
203
- }
204
- break;
205
- }
206
-
207
- case 'response': {
208
- const peer = findPeer(clientId);
209
- if (peer && peer.ws.readyState === WebSocket.OPEN) {
210
- peer.ws.send(
211
- JSON.stringify({
212
- ...message,
213
- from: clientId,
214
- timestamp: Date.now(),
215
- }),
216
- );
217
- console.log(
218
- chalk.hex('#A855F7')(' ← ') +
219
- chalk.gray('Response forwarded to mobile'),
220
- );
221
- }
222
119
  break;
223
120
  }
224
121
 
225
- case 'ping': {
226
- ws.send(
227
- JSON.stringify({
228
- type: 'pong',
229
- payload: {},
230
- timestamp: Date.now(),
231
- }),
122
+ case 'error': {
123
+ console.log(
124
+ chalk.hex('#EF4444')(' ✗ ') +
125
+ chalk.gray(message.payload.message || 'Unknown error'),
232
126
  );
233
127
  break;
234
128
  }
235
129
  }
236
130
  } catch {
237
- ws.send(
238
- JSON.stringify({
239
- type: 'error',
240
- payload: { message: 'Invalid message format' },
241
- }),
242
- );
131
+ console.warn(chalk.gray(' Invalid message received'));
243
132
  }
244
133
  });
245
134
 
246
135
  ws.on('close', () => {
247
- const client = clients.get(clientId);
248
- if (client) {
249
- const peer = findPeer(clientId);
250
- if (peer && peer.ws.readyState === WebSocket.OPEN) {
251
- peer.ws.send(
252
- JSON.stringify({
253
- type: 'status',
254
- payload: { status: 'disconnected' },
255
- timestamp: Date.now(),
256
- }),
257
- );
258
- }
259
- console.log(
260
- chalk.hex('#EF4444')(' ✗ ') +
261
- chalk.gray(`${client.type} device disconnected`),
262
- );
263
- clients.delete(clientId);
264
- }
136
+ console.log(
137
+ chalk.hex('#EF4444')(' ✗ ') +
138
+ chalk.gray('Disconnected from bridge server'),
139
+ );
140
+ scheduleReconnect();
265
141
  });
266
- });
267
142
 
268
- // Heartbeat
269
- const heartbeat = setInterval(() => {
270
- clients.forEach(client => {
271
- if (client.ws.readyState === WebSocket.OPEN) {
272
- client.ws.ping();
273
- }
143
+ ws.on('error', (err) => {
144
+ console.error(
145
+ chalk.hex('#EF4444')(' ✗ ') +
146
+ chalk.gray(`Connection error: ${err.message}`),
147
+ );
274
148
  });
275
- }, 30000);
149
+ }
150
+
151
+ function scheduleReconnect() {
152
+ if (reconnectTimer) return;
153
+ console.log(chalk.gray(' Reconnecting in 3s...'));
154
+ reconnectTimer = setTimeout(() => {
155
+ reconnectTimer = null;
156
+ connect();
157
+ }, 3000);
158
+ }
276
159
 
277
160
  banner();
161
+ connect();
278
162
 
279
163
  process.on('SIGINT', () => {
280
164
  console.log('');
281
165
  console.log(chalk.gray(' Bridge stopped.'));
282
- clearInterval(heartbeat);
283
- wss.close();
166
+ if (reconnectTimer) clearTimeout(reconnectTimer);
167
+ ws?.close();
284
168
  process.exit(0);
285
169
  });