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.
- package/dist/index.js +64 -140
- package/package.json +2 -4
- 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
|
|
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
|
-
|
|
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
|
-
}
|
|
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('
|
|
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('
|
|
34
|
+
console.log(chalk_1.default.gray(' Connecting to bridge server...'));
|
|
64
35
|
console.log('');
|
|
65
36
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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 '
|
|
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
|
-
}));
|
|
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(
|
|
58
|
+
chalk_1.default.white('Mobile device paired successfully!'));
|
|
122
59
|
}
|
|
123
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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 '
|
|
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: '
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
97
|
+
console.log(chalk_1.default.hex('#EF4444')(' ✗ ') +
|
|
98
|
+
chalk_1.default.gray('Disconnected from bridge server'));
|
|
99
|
+
scheduleReconnect();
|
|
183
100
|
});
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
198
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
}
|
|
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('
|
|
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('
|
|
52
|
+
console.log(chalk.gray(' Connecting to bridge server...'));
|
|
102
53
|
console.log('');
|
|
103
54
|
}
|
|
104
55
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
79
|
+
const message = JSON.parse(raw.toString());
|
|
122
80
|
|
|
123
81
|
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
|
-
}),
|
|
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(
|
|
94
|
+
chalk.gray('Mobile device disconnected'),
|
|
142
95
|
);
|
|
143
|
-
return;
|
|
144
96
|
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
145
99
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
108
|
+
// Send response back
|
|
109
|
+
ws!.send(
|
|
156
110
|
JSON.stringify({
|
|
157
|
-
type: '
|
|
111
|
+
type: 'response',
|
|
158
112
|
payload: {
|
|
159
|
-
status:
|
|
160
|
-
|
|
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 '
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
136
|
+
console.log(
|
|
137
|
+
chalk.hex('#EF4444')(' ✗ ') +
|
|
138
|
+
chalk.gray('Disconnected from bridge server'),
|
|
139
|
+
);
|
|
140
|
+
scheduleReconnect();
|
|
265
141
|
});
|
|
266
|
-
});
|
|
267
142
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
143
|
+
ws.on('error', (err) => {
|
|
144
|
+
console.error(
|
|
145
|
+
chalk.hex('#EF4444')(' ✗ ') +
|
|
146
|
+
chalk.gray(`Connection error: ${err.message}`),
|
|
147
|
+
);
|
|
274
148
|
});
|
|
275
|
-
}
|
|
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
|
-
|
|
283
|
-
|
|
166
|
+
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
167
|
+
ws?.close();
|
|
284
168
|
process.exit(0);
|
|
285
169
|
});
|