cursor-bridge 1.1.0 → 1.3.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 +101 -140
- package/package.json +2 -4
- package/src/index.ts +122 -194
package/dist/index.js
CHANGED
|
@@ -4,44 +4,53 @@ 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
|
|
7
|
+
const ws_1 = __importDefault(require("ws"));
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
//
|
|
10
|
+
const BRIDGE_URL = 'wss://cursor-226b2ae97542.herokuapp.com';
|
|
11
|
+
function sendToCursor(prompt) {
|
|
12
|
+
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'));
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
|
|
44
|
+
chalk_1.default.gray('Failed to send to Cursor. Is it running?'));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
14
47
|
function generatePairCode() {
|
|
15
48
|
return Math.floor(100000 + Math.random() * 900000).toString();
|
|
16
49
|
}
|
|
17
50
|
const PAIR_CODE = generatePairCode();
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
}
|
|
51
|
+
let ws = null;
|
|
52
|
+
let reconnectTimer = null;
|
|
43
53
|
function banner() {
|
|
44
|
-
const ip = getLocalIP();
|
|
45
54
|
console.clear();
|
|
46
55
|
console.log('');
|
|
47
56
|
console.log(chalk_1.default.hex('#A855F7').bold(' ╔══════════════════════════════════════╗'));
|
|
@@ -56,145 +65,97 @@ function banner() {
|
|
|
56
65
|
chalk_1.default.bgHex('#A855F7').white.bold(` ${PAIR_CODE.split('').join(' ')} `));
|
|
57
66
|
console.log('');
|
|
58
67
|
console.log(chalk_1.default.gray(' ─────────────────────────────────────────'));
|
|
59
|
-
console.log(chalk_1.default.gray('
|
|
60
|
-
console.log(chalk_1.default.gray(' Port: ') + chalk_1.default.white(String(PORT)));
|
|
68
|
+
console.log(chalk_1.default.gray(' Bridge: ') + chalk_1.default.white(BRIDGE_URL));
|
|
61
69
|
console.log(chalk_1.default.gray(' ─────────────────────────────────────────'));
|
|
62
70
|
console.log('');
|
|
63
|
-
console.log(chalk_1.default.gray('
|
|
71
|
+
console.log(chalk_1.default.gray(' Connecting to bridge server...'));
|
|
64
72
|
console.log('');
|
|
65
73
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
function connect() {
|
|
75
|
+
ws = new ws_1.default(BRIDGE_URL);
|
|
76
|
+
ws.on('open', () => {
|
|
77
|
+
console.log(chalk_1.default.hex('#22C55E')(' ✓ ') +
|
|
78
|
+
chalk_1.default.white('Connected to bridge server'));
|
|
79
|
+
ws.send(JSON.stringify({
|
|
80
|
+
type: 'pair',
|
|
81
|
+
payload: { pairCode: PAIR_CODE, clientType: 'desktop' },
|
|
82
|
+
timestamp: Date.now(),
|
|
83
|
+
}));
|
|
84
|
+
console.log(chalk_1.default.gray(' Waiting for mobile device...'));
|
|
85
|
+
console.log('');
|
|
75
86
|
});
|
|
76
87
|
ws.on('message', (raw) => {
|
|
77
88
|
try {
|
|
78
89
|
const message = JSON.parse(raw.toString());
|
|
79
90
|
switch (message.type) {
|
|
80
|
-
case '
|
|
81
|
-
const
|
|
82
|
-
|
|
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
|
-
}));
|
|
91
|
+
case 'status': {
|
|
92
|
+
const status = message.payload.status;
|
|
93
|
+
if (status === 'connected') {
|
|
116
94
|
console.log(chalk_1.default.hex('#22C55E')(' ✓ ') +
|
|
117
|
-
chalk_1.default.white(
|
|
95
|
+
chalk_1.default.white('Mobile device paired successfully!'));
|
|
118
96
|
}
|
|
119
|
-
else {
|
|
120
|
-
console.log(chalk_1.default.
|
|
121
|
-
chalk_1.default.white(`${clientType} connected with pair code`));
|
|
97
|
+
else if (status === 'waiting') {
|
|
98
|
+
console.log(chalk_1.default.gray(' Waiting for mobile device...'));
|
|
122
99
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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'));
|
|
135
|
-
}
|
|
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'));
|
|
100
|
+
else if (status === 'disconnected') {
|
|
101
|
+
console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
|
|
102
|
+
chalk_1.default.gray('Mobile device disconnected'));
|
|
148
103
|
}
|
|
149
104
|
break;
|
|
150
105
|
}
|
|
151
|
-
case '
|
|
106
|
+
case 'command': {
|
|
107
|
+
const prompt = message.payload.prompt;
|
|
108
|
+
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);
|
|
152
112
|
ws.send(JSON.stringify({
|
|
153
|
-
type: '
|
|
154
|
-
payload: {
|
|
113
|
+
type: 'response',
|
|
114
|
+
payload: {
|
|
115
|
+
status: 'sent',
|
|
116
|
+
message: 'Sent to Cursor IDE',
|
|
117
|
+
},
|
|
155
118
|
timestamp: Date.now(),
|
|
156
119
|
}));
|
|
157
120
|
break;
|
|
158
121
|
}
|
|
122
|
+
case 'error': {
|
|
123
|
+
console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
|
|
124
|
+
chalk_1.default.gray(message.payload.message || 'Unknown error'));
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
159
127
|
}
|
|
160
128
|
}
|
|
161
129
|
catch {
|
|
162
|
-
|
|
163
|
-
type: 'error',
|
|
164
|
-
payload: { message: 'Invalid message format' },
|
|
165
|
-
}));
|
|
130
|
+
console.warn(chalk_1.default.gray(' Invalid message received'));
|
|
166
131
|
}
|
|
167
132
|
});
|
|
168
133
|
ws.on('close', () => {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
}
|
|
134
|
+
console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
|
|
135
|
+
chalk_1.default.gray('Disconnected from bridge server'));
|
|
136
|
+
scheduleReconnect();
|
|
183
137
|
});
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
clients.forEach(client => {
|
|
188
|
-
if (client.ws.readyState === ws_1.WebSocket.OPEN) {
|
|
189
|
-
client.ws.ping();
|
|
190
|
-
}
|
|
138
|
+
ws.on('error', (err) => {
|
|
139
|
+
console.error(chalk_1.default.hex('#EF4444')(' ✗ ') +
|
|
140
|
+
chalk_1.default.gray(`Connection error: ${err.message}`));
|
|
191
141
|
});
|
|
192
|
-
}
|
|
142
|
+
}
|
|
143
|
+
function scheduleReconnect() {
|
|
144
|
+
if (reconnectTimer)
|
|
145
|
+
return;
|
|
146
|
+
console.log(chalk_1.default.gray(' Reconnecting in 3s...'));
|
|
147
|
+
reconnectTimer = setTimeout(() => {
|
|
148
|
+
reconnectTimer = null;
|
|
149
|
+
connect();
|
|
150
|
+
}, 3000);
|
|
151
|
+
}
|
|
193
152
|
banner();
|
|
153
|
+
connect();
|
|
194
154
|
process.on('SIGINT', () => {
|
|
195
155
|
console.log('');
|
|
196
156
|
console.log(chalk_1.default.gray(' Bridge stopped.'));
|
|
197
|
-
|
|
198
|
-
|
|
157
|
+
if (reconnectTimer)
|
|
158
|
+
clearTimeout(reconnectTimer);
|
|
159
|
+
ws?.close();
|
|
199
160
|
process.exit(0);
|
|
200
161
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursor-bridge",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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,64 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
3
|
+
import WebSocket from 'ws';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
|
-
import os from 'os';
|
|
7
6
|
|
|
8
|
-
const
|
|
9
|
-
|
|
7
|
+
const BRIDGE_URL = 'wss://cursor-226b2ae97542.herokuapp.com';
|
|
8
|
+
|
|
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
|
+
});
|
|
39
|
+
|
|
40
|
+
console.log(
|
|
41
|
+
chalk.hex('#22C55E')(' ✓ ') +
|
|
42
|
+
chalk.white('Sent to Cursor IDE'),
|
|
43
|
+
);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
console.log(
|
|
46
|
+
chalk.hex('#EF4444')(' ✗ ') +
|
|
47
|
+
chalk.gray('Failed to send to Cursor. Is it running?'),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
10
51
|
|
|
11
|
-
// 6 haneli pair code üret
|
|
12
52
|
function generatePairCode(): string {
|
|
13
53
|
return Math.floor(100000 + Math.random() * 900000).toString();
|
|
14
54
|
}
|
|
15
55
|
|
|
16
56
|
const PAIR_CODE = generatePairCode();
|
|
17
57
|
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
}
|
|
58
|
+
let ws: WebSocket | null = null;
|
|
59
|
+
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
61
60
|
|
|
62
61
|
function banner() {
|
|
63
|
-
const ip = getLocalIP();
|
|
64
|
-
|
|
65
62
|
console.clear();
|
|
66
63
|
console.log('');
|
|
67
64
|
console.log(
|
|
@@ -89,197 +86,128 @@ function banner() {
|
|
|
89
86
|
chalk.gray(' ─────────────────────────────────────────'),
|
|
90
87
|
);
|
|
91
88
|
console.log(
|
|
92
|
-
chalk.gray('
|
|
93
|
-
);
|
|
94
|
-
console.log(
|
|
95
|
-
chalk.gray(' Port: ') + chalk.white(String(PORT)),
|
|
89
|
+
chalk.gray(' Bridge: ') + chalk.white(BRIDGE_URL),
|
|
96
90
|
);
|
|
97
91
|
console.log(
|
|
98
92
|
chalk.gray(' ─────────────────────────────────────────'),
|
|
99
93
|
);
|
|
100
94
|
console.log('');
|
|
101
|
-
console.log(chalk.gray('
|
|
95
|
+
console.log(chalk.gray(' Connecting to bridge server...'));
|
|
102
96
|
console.log('');
|
|
103
97
|
}
|
|
104
98
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
99
|
+
function connect() {
|
|
100
|
+
ws = new WebSocket(BRIDGE_URL);
|
|
101
|
+
|
|
102
|
+
ws.on('open', () => {
|
|
103
|
+
console.log(
|
|
104
|
+
chalk.hex('#22C55E')(' ✓ ') +
|
|
105
|
+
chalk.white('Connected to bridge server'),
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
ws!.send(
|
|
109
|
+
JSON.stringify({
|
|
110
|
+
type: 'pair',
|
|
111
|
+
payload: { pairCode: PAIR_CODE, clientType: 'desktop' },
|
|
112
|
+
timestamp: Date.now(),
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
console.log(chalk.gray(' Waiting for mobile device...'));
|
|
117
|
+
console.log('');
|
|
117
118
|
});
|
|
118
119
|
|
|
119
120
|
ws.on('message', (raw: Buffer) => {
|
|
120
121
|
try {
|
|
121
|
-
const message
|
|
122
|
+
const message = JSON.parse(raw.toString());
|
|
122
123
|
|
|
123
124
|
switch (message.type) {
|
|
124
|
-
case '
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
}),
|
|
125
|
+
case 'status': {
|
|
126
|
+
const status = message.payload.status as string;
|
|
127
|
+
if (status === 'connected') {
|
|
128
|
+
console.log(
|
|
129
|
+
chalk.hex('#22C55E')(' ✓ ') +
|
|
130
|
+
chalk.white('Mobile device paired successfully!'),
|
|
138
131
|
);
|
|
132
|
+
} else if (status === 'waiting') {
|
|
133
|
+
console.log(chalk.gray(' Waiting for mobile device...'));
|
|
134
|
+
} else if (status === 'disconnected') {
|
|
139
135
|
console.log(
|
|
140
136
|
chalk.hex('#EF4444')(' ✗ ') +
|
|
141
|
-
chalk.gray(
|
|
137
|
+
chalk.gray('Mobile device disconnected'),
|
|
142
138
|
);
|
|
143
|
-
return;
|
|
144
139
|
}
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
145
142
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
143
|
+
case 'command': {
|
|
144
|
+
const prompt = message.payload.prompt as string;
|
|
145
|
+
console.log(
|
|
146
|
+
chalk.hex('#A855F7')(' → ') +
|
|
147
|
+
chalk.white('Command received: ') +
|
|
148
|
+
chalk.gray(prompt.substring(0, 80) + (prompt.length > 80 ? '...' : '')),
|
|
149
|
+
);
|
|
152
150
|
|
|
153
|
-
|
|
151
|
+
sendToCursor(prompt);
|
|
154
152
|
|
|
155
|
-
ws
|
|
153
|
+
ws!.send(
|
|
156
154
|
JSON.stringify({
|
|
157
|
-
type: '
|
|
155
|
+
type: 'response',
|
|
158
156
|
payload: {
|
|
159
|
-
status:
|
|
160
|
-
|
|
161
|
-
host: getLocalIP(),
|
|
162
|
-
port: PORT,
|
|
157
|
+
status: 'sent',
|
|
158
|
+
message: 'Sent to Cursor IDE',
|
|
163
159
|
},
|
|
164
160
|
timestamp: Date.now(),
|
|
165
161
|
}),
|
|
166
162
|
);
|
|
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
163
|
break;
|
|
223
164
|
}
|
|
224
165
|
|
|
225
|
-
case '
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
payload: {},
|
|
230
|
-
timestamp: Date.now(),
|
|
231
|
-
}),
|
|
166
|
+
case 'error': {
|
|
167
|
+
console.log(
|
|
168
|
+
chalk.hex('#EF4444')(' ✗ ') +
|
|
169
|
+
chalk.gray(message.payload.message || 'Unknown error'),
|
|
232
170
|
);
|
|
233
171
|
break;
|
|
234
172
|
}
|
|
235
173
|
}
|
|
236
174
|
} catch {
|
|
237
|
-
|
|
238
|
-
JSON.stringify({
|
|
239
|
-
type: 'error',
|
|
240
|
-
payload: { message: 'Invalid message format' },
|
|
241
|
-
}),
|
|
242
|
-
);
|
|
175
|
+
console.warn(chalk.gray(' Invalid message received'));
|
|
243
176
|
}
|
|
244
177
|
});
|
|
245
178
|
|
|
246
179
|
ws.on('close', () => {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
}
|
|
180
|
+
console.log(
|
|
181
|
+
chalk.hex('#EF4444')(' ✗ ') +
|
|
182
|
+
chalk.gray('Disconnected from bridge server'),
|
|
183
|
+
);
|
|
184
|
+
scheduleReconnect();
|
|
265
185
|
});
|
|
266
|
-
});
|
|
267
186
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
187
|
+
ws.on('error', (err) => {
|
|
188
|
+
console.error(
|
|
189
|
+
chalk.hex('#EF4444')(' ✗ ') +
|
|
190
|
+
chalk.gray(`Connection error: ${err.message}`),
|
|
191
|
+
);
|
|
274
192
|
});
|
|
275
|
-
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function scheduleReconnect() {
|
|
196
|
+
if (reconnectTimer) return;
|
|
197
|
+
console.log(chalk.gray(' Reconnecting in 3s...'));
|
|
198
|
+
reconnectTimer = setTimeout(() => {
|
|
199
|
+
reconnectTimer = null;
|
|
200
|
+
connect();
|
|
201
|
+
}, 3000);
|
|
202
|
+
}
|
|
276
203
|
|
|
277
204
|
banner();
|
|
205
|
+
connect();
|
|
278
206
|
|
|
279
207
|
process.on('SIGINT', () => {
|
|
280
208
|
console.log('');
|
|
281
209
|
console.log(chalk.gray(' Bridge stopped.'));
|
|
282
|
-
|
|
283
|
-
|
|
210
|
+
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
211
|
+
ws?.close();
|
|
284
212
|
process.exit(0);
|
|
285
213
|
});
|