cursorconnect 0.1.4 → 0.1.6
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/README.md +2 -2
- package/bridge-runtime/connector-version.json +1 -0
- package/bridge-runtime/dist/connector-client-version.d.ts +2 -0
- package/bridge-runtime/dist/connector-client-version.js +43 -0
- package/bridge-runtime/dist/index.js +3 -0
- package/bridge-runtime/dist/relay-upstream.js +9 -1
- package/bridge-runtime/dist/relay.js +1 -1
- package/dist/big-code.js +36 -5
- package/dist/bridge-dir.js +1 -1
- package/dist/bundled-bridge-check.js +25 -0
- package/dist/diagnose.js +4 -4
- package/dist/index.js +37 -56
- package/dist/launch.js +36 -1
- package/dist/pairing-identity.js +3 -2
- package/dist/pairing-ttl.js +3 -0
- package/dist/print-pairing.js +19 -28
- package/dist/repo-root.js +2 -2
- package/package.json +2 -2
- package/version-policy.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# cursorconnect
|
|
2
2
|
|
|
3
|
-
CLI для Mac:
|
|
3
|
+
CLI для Mac: Cursor Connect в фоне, pairing (код), Cursor с CDP.
|
|
4
4
|
|
|
5
5
|
## Установка (пользователи)
|
|
6
6
|
|
|
@@ -24,7 +24,7 @@ npm run install:cli
|
|
|
24
24
|
## Команды
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
cursorconnect start # CDP (авто Cmd+Q + Cursor с :9222) +
|
|
27
|
+
cursorconnect start # CDP (авто Cmd+Q + Cursor с :9222) + Cursor Connect + код
|
|
28
28
|
cursorconnect start -r # то же (явный перезапуск)
|
|
29
29
|
cursorconnect start --no-restart-cursor # не трогать Cursor (спросит y/n)
|
|
30
30
|
cursorconnect status
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"cliVersion":"0.1.6","bundledAt":"2026-05-25T09:55:59Z"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
const MIN_FALLBACK = '0.1.5';
|
|
5
|
+
function readJsonVersion(path) {
|
|
6
|
+
if (!existsSync(path))
|
|
7
|
+
return null;
|
|
8
|
+
try {
|
|
9
|
+
const raw = JSON.parse(readFileSync(path, 'utf-8'));
|
|
10
|
+
const v = raw.cliVersion?.trim() || raw.version?.trim();
|
|
11
|
+
return v || null;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** Версия для relay auth (должна быть ≥ MIN_CLI_VERSION на VPS). */
|
|
18
|
+
export function connectorClientVersion() {
|
|
19
|
+
const fromEnv = process.env.CURSORCONNECT_CLI_VERSION?.trim();
|
|
20
|
+
if (fromEnv && fromEnv !== '0.0.0')
|
|
21
|
+
return fromEnv;
|
|
22
|
+
const bridgeRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
23
|
+
const fromBundle = readJsonVersion(join(bridgeRoot, 'connector-version.json'));
|
|
24
|
+
if (fromBundle)
|
|
25
|
+
return fromBundle;
|
|
26
|
+
const candidates = [
|
|
27
|
+
join(bridgeRoot, '..', 'connect', 'package.json'),
|
|
28
|
+
join(bridgeRoot, 'package.json'),
|
|
29
|
+
];
|
|
30
|
+
for (const p of candidates) {
|
|
31
|
+
if (!existsSync(p))
|
|
32
|
+
continue;
|
|
33
|
+
try {
|
|
34
|
+
const pkg = JSON.parse(readFileSync(p, 'utf-8'));
|
|
35
|
+
if (pkg.version?.trim())
|
|
36
|
+
return pkg.version.trim();
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
/* next */
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return MIN_FALLBACK;
|
|
43
|
+
}
|
|
@@ -7,10 +7,13 @@ import { JsonlIndex } from './jsonl-index.js';
|
|
|
7
7
|
import { WindowMonitor } from './window-monitor.js';
|
|
8
8
|
import { Relay } from './relay.js';
|
|
9
9
|
import { MessageDebugStore } from './message-debug-store.js';
|
|
10
|
+
import { connectorClientVersion } from './connector-client-version.js';
|
|
10
11
|
async function main() {
|
|
11
12
|
const config = loadConfig();
|
|
12
13
|
const selectors = loadSelectors();
|
|
14
|
+
const connectorVersion = connectorClientVersion();
|
|
13
15
|
console.log('=== CursorConnect Bridge ===');
|
|
16
|
+
console.log(`Connector version: ${connectorVersion} (env=${process.env.CURSORCONNECT_CLI_VERSION?.trim() || '-'})`);
|
|
14
17
|
console.log(`CDP: ${config.cdpUrl}`);
|
|
15
18
|
console.log(`Server: http://${config.serverHost}:${config.serverPort}`);
|
|
16
19
|
if (config.relayUrl) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { io } from 'socket.io-client';
|
|
2
|
+
import { connectorClientVersion } from './connector-client-version.js';
|
|
2
3
|
import { loadPairingIdentity } from './pairing-identity.js';
|
|
3
4
|
const CLIENT_EVENTS = [
|
|
4
5
|
'command',
|
|
@@ -51,6 +52,8 @@ export class RelayUpstream {
|
|
|
51
52
|
role: 'connector',
|
|
52
53
|
token: this.config.relayToken,
|
|
53
54
|
roomId: this.config.relayRoomId,
|
|
55
|
+
clientKind: 'connector',
|
|
56
|
+
clientVersion: connectorClientVersion(),
|
|
54
57
|
},
|
|
55
58
|
});
|
|
56
59
|
this.socket.on('connect', () => {
|
|
@@ -58,7 +61,12 @@ export class RelayUpstream {
|
|
|
58
61
|
this.registerPairing();
|
|
59
62
|
});
|
|
60
63
|
this.socket.on('connect_error', (err) => {
|
|
61
|
-
|
|
64
|
+
const v = connectorClientVersion();
|
|
65
|
+
console.warn(`[relay-upstream] connect_error: ${err.message} (clientVersion=${v})`);
|
|
66
|
+
if (String(err.message).includes('CLI_VERSION_OUTDATED') ||
|
|
67
|
+
String(err.message).includes('CLI_VERSION_MISSING')) {
|
|
68
|
+
console.warn('[relay-upstream] Обновите cursorconnect: npm install -g cursorconnect@latest && cursorconnect start');
|
|
69
|
+
}
|
|
62
70
|
});
|
|
63
71
|
this.socket.on('disconnect', (reason) => {
|
|
64
72
|
console.log('[relay-upstream] disconnected:', reason);
|
|
@@ -243,7 +243,7 @@ export class Relay {
|
|
|
243
243
|
const apiKey = this.config.openaiApiKey;
|
|
244
244
|
if (!apiKey) {
|
|
245
245
|
res.status(503).json({
|
|
246
|
-
error: 'OPENAI_API_KEY not set on
|
|
246
|
+
error: 'OPENAI_API_KEY not set on Cursor Connect Mac (add to bridge/.env)',
|
|
247
247
|
});
|
|
248
248
|
return;
|
|
249
249
|
}
|
package/dist/big-code.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/** 5×5 block
|
|
2
|
-
const
|
|
1
|
+
/** 5×5 block glyphs (█) for pairing code — A–Z and 0–9. */
|
|
2
|
+
const GLYPHS = {
|
|
3
3
|
'0': ['█████', '█ █', '█ █', '█ █', '█████'],
|
|
4
4
|
'1': [' █ ', ' ██ ', ' █ ', ' █ ', ' ███ '],
|
|
5
5
|
'2': ['█████', ' █', '█████', '█ ', '█████'],
|
|
@@ -10,13 +10,44 @@ const DIGITS = {
|
|
|
10
10
|
'7': ['█████', ' █', ' █ ', ' █ ', ' █ '],
|
|
11
11
|
'8': ['█████', '█ █', '█████', '█ █', '█████'],
|
|
12
12
|
'9': ['█████', '█ █', '█████', ' █', '█████'],
|
|
13
|
+
A: [' ███ ', '█ █', '█████', '█ █', '█ █'],
|
|
14
|
+
B: ['████ ', '█ █', '████ ', '█ █', '████ '],
|
|
15
|
+
C: [' ████', '█ ', '█ ', '█ ', ' ████'],
|
|
16
|
+
D: ['████ ', '█ █', '█ █', '█ █', '████ '],
|
|
17
|
+
E: ['█████', '█ ', '████ ', '█ ', '█████'],
|
|
18
|
+
F: ['█████', '█ ', '████ ', '█ ', '█ '],
|
|
19
|
+
G: [' ████', '█ ', '█ ██', '█ █', ' ████'],
|
|
20
|
+
H: ['█ █', '█ █', '█████', '█ █', '█ █'],
|
|
21
|
+
I: [' ███ ', ' █ ', ' █ ', ' █ ', ' ███ '],
|
|
22
|
+
J: [' ███', ' █', ' █', '█ █', ' ███ '],
|
|
23
|
+
K: ['█ █', '█ █ ', '███ ', '█ █ ', '█ █'],
|
|
24
|
+
L: ['█ ', '█ ', '█ ', '█ ', '█████'],
|
|
25
|
+
M: ['█ █', '██ ██', '█ █ █', '█ █', '█ █'],
|
|
26
|
+
N: ['█ █', '██ █', '█ █ █', '█ ██', '█ █'],
|
|
27
|
+
O: [' ███ ', '█ █', '█ █', '█ █', ' ███ '],
|
|
28
|
+
P: ['████ ', '█ █', '████ ', '█ ', '█ '],
|
|
29
|
+
Q: [' ███ ', '█ █', '█ █', '█ ██', ' ████'],
|
|
30
|
+
R: ['████ ', '█ █', '████ ', '█ █ ', '█ █'],
|
|
31
|
+
S: [' ████', '█ ', ' ███ ', ' █', '████ '],
|
|
32
|
+
T: ['█████', ' █ ', ' █ ', ' █ ', ' █ '],
|
|
33
|
+
U: ['█ █', '█ █', '█ █', '█ █', ' ███ '],
|
|
34
|
+
V: ['█ █', '█ █', '█ █', ' █ █ ', ' █ '],
|
|
35
|
+
W: ['█ █', '█ █', '█ █ █', '██ ██', '█ █'],
|
|
36
|
+
X: ['█ █', ' █ █ ', ' █ ', ' █ █ ', '█ █'],
|
|
37
|
+
Y: ['█ █', ' █ █ ', ' █ ', ' █ ', ' █ '],
|
|
38
|
+
Z: ['█████', ' █ ', ' █ ', ' █ ', '█████'],
|
|
13
39
|
};
|
|
40
|
+
const FALLBACK = GLYPHS['8'];
|
|
14
41
|
const GAP = ' ';
|
|
15
42
|
export function renderBigCode(code) {
|
|
16
|
-
const
|
|
43
|
+
const chars = code
|
|
44
|
+
.toUpperCase()
|
|
45
|
+
.replace(/[^A-Z0-9]/g, '')
|
|
46
|
+
.slice(0, 6)
|
|
47
|
+
.split('');
|
|
17
48
|
const rows = ['', '', '', '', ''];
|
|
18
|
-
for (const ch of
|
|
19
|
-
const art =
|
|
49
|
+
for (const ch of chars) {
|
|
50
|
+
const art = GLYPHS[ch] ?? FALLBACK;
|
|
20
51
|
for (let i = 0; i < 5; i++) {
|
|
21
52
|
rows[i] += art[i] + GAP;
|
|
22
53
|
}
|
package/dist/bridge-dir.js
CHANGED
|
@@ -51,7 +51,7 @@ export function resolveBridgeDir() {
|
|
|
51
51
|
if (isValidBridgeDir(bridge))
|
|
52
52
|
return bridge;
|
|
53
53
|
}
|
|
54
|
-
throw new Error('Не найден bridge.\n' +
|
|
54
|
+
throw new Error('Не найден Cursor Connect (bridge).\n' +
|
|
55
55
|
' npm install -g cursorconnect && cursorconnect start\n' +
|
|
56
56
|
' (для разработки: клон репо + npm install в корне)');
|
|
57
57
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { resolveBridgeDir } from './bridge-dir.js';
|
|
4
|
+
export function bundledBridgeHasClientVersion() {
|
|
5
|
+
let bridgeDir;
|
|
6
|
+
try {
|
|
7
|
+
bridgeDir = resolveBridgeDir();
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
const relayUpstream = join(bridgeDir, 'dist/relay-upstream.js');
|
|
13
|
+
if (!existsSync(relayUpstream))
|
|
14
|
+
return true;
|
|
15
|
+
return readFileSync(relayUpstream, 'utf8').includes('clientVersion');
|
|
16
|
+
}
|
|
17
|
+
/** npm-пакет должен включать bridge с auth.clientVersion (иначе relay отклоняет connector). */
|
|
18
|
+
export function assertBundledBridgeSupportsRelay() {
|
|
19
|
+
if (bundledBridgeHasClientVersion())
|
|
20
|
+
return;
|
|
21
|
+
console.error('[cursorconnect] Устаревший bridge внутри npm-пакета (нет clientVersion для relay).\n' +
|
|
22
|
+
' npm install -g cursorconnect@latest\n' +
|
|
23
|
+
' Если уже latest — дождитесь публикации fix или запускайте из клона: npm run bundle-bridge -w connect');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
package/dist/diagnose.js
CHANGED
|
@@ -117,7 +117,7 @@ export async function runDiagnose() {
|
|
|
117
117
|
ok: ok && Boolean(j.connectorInRoom) && Boolean(j.tokenRegistered),
|
|
118
118
|
detail: JSON.stringify(j),
|
|
119
119
|
hint: !j.connectorInRoom
|
|
120
|
-
? '
|
|
120
|
+
? 'Cursor Connect не в этой комнате на relay'
|
|
121
121
|
: !j.tokenRegistered
|
|
122
122
|
? 'Токен не зарегистрирован — перезапустите cursorconnect start'
|
|
123
123
|
: !j.tokenAccepted
|
|
@@ -138,7 +138,7 @@ export async function runDiagnose() {
|
|
|
138
138
|
const res = await fetch('http://127.0.0.1:3847/health');
|
|
139
139
|
const j = (await res.json());
|
|
140
140
|
checks.push({
|
|
141
|
-
id: '
|
|
141
|
+
id: 'cursor-connect.local',
|
|
142
142
|
ok: Boolean(j.ok),
|
|
143
143
|
detail: `ok=${j.ok} cdp=${j.cdp}`,
|
|
144
144
|
hint: j.cdp ? undefined : 'Cursor с --remote-debugging-port=9222',
|
|
@@ -146,7 +146,7 @@ export async function runDiagnose() {
|
|
|
146
146
|
}
|
|
147
147
|
catch (e) {
|
|
148
148
|
checks.push({
|
|
149
|
-
id: '
|
|
149
|
+
id: 'cursor-connect.local',
|
|
150
150
|
ok: false,
|
|
151
151
|
detail: e.message,
|
|
152
152
|
hint: 'cursorconnect start',
|
|
@@ -201,7 +201,7 @@ export async function runDiagnose() {
|
|
|
201
201
|
id: 'socket.history',
|
|
202
202
|
ok: history.ok,
|
|
203
203
|
detail: history.detail,
|
|
204
|
-
hint: history.ok ? undefined : 'connectorInRoom false или
|
|
204
|
+
hint: history.ok ? undefined : 'connectorInRoom false или Cursor Connect не отвечает — cursorconnect start',
|
|
205
205
|
});
|
|
206
206
|
return { at: new Date().toISOString(), relayUrl, roomId, checks };
|
|
207
207
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { join, resolve } from 'path';
|
|
3
3
|
import { ensurePairingIdentity, loadPairingIdentity, refreshPairingCode, } from './pairing-identity.js';
|
|
4
|
-
import { ensureCursorCdp, isBridgeRunning, startBridge, stopBridge, waitBridgeHealth,
|
|
4
|
+
import { ensureCursorCdp, isBridgeRunning, startBridge, stopBridge, waitBridgeHealth, waitRelayPairingReady, } from './launch.js';
|
|
5
5
|
import { printPairingToTerminal } from './print-pairing.js';
|
|
6
6
|
import { BRIDGE_LOG_FILE } from './paths.js';
|
|
7
7
|
import { ensureUserConfig, isValidBridgeDir, resolveBridgeDir, USER_CONFIG_ENV, } from './bridge-dir.js';
|
|
@@ -9,6 +9,7 @@ import { configFilePath, saveInstallConfig } from './repo-root.js';
|
|
|
9
9
|
import { formatDiagnoseReport, runDiagnose } from './diagnose.js';
|
|
10
10
|
import { loadEnvFile, resolveRelayConfig } from './relay-config.js';
|
|
11
11
|
import { ensureCliVersionForRelay } from './version-check.js';
|
|
12
|
+
import { assertBundledBridgeSupportsRelay, bundledBridgeHasClientVersion, } from './bundled-bridge-check.js';
|
|
12
13
|
import { CLI_VERSION } from './cli-version.js';
|
|
13
14
|
function bridgeEnv(identity) {
|
|
14
15
|
ensureUserConfig();
|
|
@@ -41,6 +42,7 @@ function startFlags(argv) {
|
|
|
41
42
|
};
|
|
42
43
|
}
|
|
43
44
|
async function cmdStart(argv) {
|
|
45
|
+
assertBundledBridgeSupportsRelay();
|
|
44
46
|
const { restartCursor, noRestartCursor } = startFlags(argv);
|
|
45
47
|
const identity = ensurePairingIdentity();
|
|
46
48
|
const refreshed = refreshPairingCode(identity);
|
|
@@ -54,32 +56,38 @@ async function cmdStart(argv) {
|
|
|
54
56
|
noRestartCursor,
|
|
55
57
|
});
|
|
56
58
|
if (!cdpPortOk) {
|
|
57
|
-
console.warn('[cursorconnect] CDP порт недоступен —
|
|
59
|
+
console.warn('[cursorconnect] CDP порт недоступен — Cursor Connect запустится, список чатов будет из архива JSONL.');
|
|
58
60
|
}
|
|
59
61
|
if (isBridgeRunning()) {
|
|
60
|
-
console.
|
|
62
|
+
console.error('[cursorconnect] Перезапуск Cursor Connect…');
|
|
61
63
|
stopBridge();
|
|
62
64
|
}
|
|
63
65
|
startBridge(env);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const connectorOk = await waitRelayConnector(relayUrl, 30_000);
|
|
68
|
-
const cdpReady = cdpPortOk && health.cdp;
|
|
69
|
-
if (cdpPortOk && !health.cdp) {
|
|
70
|
-
console.warn('[cursorconnect] Порт 9222 открыт, но bridge не подключился к Cursor (cdp:false). ' +
|
|
71
|
-
'Проверьте: cursorconnect status · tail -f ' +
|
|
72
|
-
BRIDGE_LOG_FILE);
|
|
66
|
+
let health = await waitBridgeHealth(12_000, { requireCdp: false });
|
|
67
|
+
if (health.ok && cdpPortOk && !health.cdp) {
|
|
68
|
+
health = await waitBridgeHealth(15_000, { requireCdp: true });
|
|
73
69
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
const pairingReady = await waitRelayPairingReady(relayUrl, refreshed.roomId, 30_000);
|
|
71
|
+
if (!pairingReady.ok) {
|
|
72
|
+
console.error('[cursorconnect] Код не зарегистрирован на relay — приложение его не примет.');
|
|
73
|
+
if (!pairingReady.connectorInRoom) {
|
|
74
|
+
console.error(' Mac не подключён к серверу. Обновите CLI: npm install -g cursorconnect@latest');
|
|
75
|
+
console.error(' Затем снова: cursorconnect start');
|
|
76
|
+
console.error(` Лог bridge: ${BRIDGE_LOG_FILE}`);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.error(' Connector в комнате, но код не на сервере — подождите и повторите start.');
|
|
80
|
+
}
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
if (cdpPortOk && health.ok && !health.cdp) {
|
|
84
|
+
console.error(`[cursorconnect] CDP не подключён (cdp:false) · лог: ${BRIDGE_LOG_FILE}`);
|
|
85
|
+
}
|
|
86
|
+
printPairingToTerminal(refreshed);
|
|
79
87
|
}
|
|
80
88
|
async function cmdStop() {
|
|
81
89
|
stopBridge();
|
|
82
|
-
console.log('
|
|
90
|
+
console.log('Cursor Connect остановлен');
|
|
83
91
|
}
|
|
84
92
|
async function cmdInit(pathArg) {
|
|
85
93
|
const target = pathArg?.trim() || process.cwd();
|
|
@@ -104,43 +112,16 @@ async function cmdDiagnose() {
|
|
|
104
112
|
async function cmdStatus() {
|
|
105
113
|
const identity = loadPairingIdentity();
|
|
106
114
|
ensureUserConfig();
|
|
107
|
-
const bridgeDotEnv = join(resolveBridgeDir(), '.env');
|
|
108
|
-
const { relayUrl } = resolveRelayConfig([bridgeDotEnv]);
|
|
109
|
-
console.log('Bridge running:', isBridgeRunning());
|
|
110
115
|
if (!identity) {
|
|
111
|
-
console.
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
let connector = false;
|
|
115
|
-
let cdp = false;
|
|
116
|
-
let bridgeOk = false;
|
|
117
|
-
try {
|
|
118
|
-
const h = await fetch('http://127.0.0.1:3847/health');
|
|
119
|
-
const j = (await h.json());
|
|
120
|
-
bridgeOk = Boolean(j.ok);
|
|
121
|
-
cdp = Boolean(j.cdp);
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
/* offline */
|
|
125
|
-
}
|
|
126
|
-
if (relayUrl) {
|
|
127
|
-
try {
|
|
128
|
-
const res = await fetch(`${relayUrl.replace(/\/$/, '')}/health`);
|
|
129
|
-
const j = (await res.json());
|
|
130
|
-
connector = Boolean(j.connector);
|
|
131
|
-
}
|
|
132
|
-
catch {
|
|
133
|
-
/* ignore */
|
|
134
|
-
}
|
|
135
|
-
printPairingToTerminal(identity, { bridge: bridgeOk, cdp, connector });
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
printPairingToTerminal(identity, {
|
|
139
|
-
bridge: bridgeOk,
|
|
140
|
-
cdp,
|
|
141
|
-
connector: false,
|
|
142
|
-
});
|
|
116
|
+
console.error('Identity не найден — запустите: cursorconnect start');
|
|
117
|
+
process.exit(1);
|
|
143
118
|
}
|
|
119
|
+
console.log(`CLI: ${CLI_VERSION}`);
|
|
120
|
+
console.log(bundledBridgeHasClientVersion()
|
|
121
|
+
? 'Bridge bundle: OK (clientVersion в relay-upstream)'
|
|
122
|
+
: 'Bridge bundle: STALE — обновите npm install -g cursorconnect@latest');
|
|
123
|
+
console.log(`Bridge process: ${isBridgeRunning() ? 'running' : 'stopped'}`);
|
|
124
|
+
printPairingToTerminal(identity);
|
|
144
125
|
}
|
|
145
126
|
async function main() {
|
|
146
127
|
const argv = process.argv.slice(2);
|
|
@@ -165,12 +146,12 @@ async function main() {
|
|
|
165
146
|
default:
|
|
166
147
|
console.log(`Usage: cursorconnect <command>
|
|
167
148
|
|
|
168
|
-
start — CDP (авто-перезапуск Cursor) +
|
|
149
|
+
start — CDP (авто-перезапуск Cursor) + Cursor Connect + код
|
|
169
150
|
start -r — то же (явный перезапуск Cursor)
|
|
170
151
|
start --no-restart-cursor — не трогать Cursor (спросит y/n в TTY)
|
|
171
|
-
stop — остановить
|
|
152
|
+
stop — остановить Cursor Connect
|
|
172
153
|
status — код и статус
|
|
173
|
-
diagnose — проверка relay /
|
|
154
|
+
diagnose — проверка relay / Cursor Connect / socket (без гаданий)
|
|
174
155
|
init [path] — только для разработки (клон репо)
|
|
175
156
|
|
|
176
157
|
Установка (из любой папки):
|
package/dist/launch.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execSync, spawn, spawnSync } from 'child_process';
|
|
2
2
|
import { askYesNo } from './ask.js';
|
|
3
|
-
import { existsSync, openSync, readFileSync, writeFileSync } from 'fs';
|
|
3
|
+
import { closeSync, existsSync, openSync, readFileSync, writeFileSync } from 'fs';
|
|
4
4
|
import { BRIDGE_LOG_FILE, BRIDGE_PID_FILE } from './paths.js';
|
|
5
5
|
import { resolveBridgeDir } from './bridge-dir.js';
|
|
6
6
|
export function isBridgeRunning() {
|
|
@@ -59,6 +59,12 @@ export function startBridge(env) {
|
|
|
59
59
|
stdio: ['ignore', logFd, logFd],
|
|
60
60
|
});
|
|
61
61
|
child.unref();
|
|
62
|
+
try {
|
|
63
|
+
closeSync(logFd);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
/* ignore */
|
|
67
|
+
}
|
|
62
68
|
writeFileSync(BRIDGE_PID_FILE, String(child.pid));
|
|
63
69
|
return child;
|
|
64
70
|
}
|
|
@@ -115,6 +121,35 @@ export async function waitRelayConnector(relayUrl, maxMs = 45_000) {
|
|
|
115
121
|
}
|
|
116
122
|
return false;
|
|
117
123
|
}
|
|
124
|
+
/** Mac в relay-комнате и pairing-код зарегистрирован (иначе app получит 404). */
|
|
125
|
+
export async function waitRelayPairingReady(relayUrl, roomId, maxMs = 45_000) {
|
|
126
|
+
const base = relayUrl.replace(/\/$/, '');
|
|
127
|
+
const deadline = Date.now() + maxMs;
|
|
128
|
+
let lastConnector = false;
|
|
129
|
+
let lastCodes = 0;
|
|
130
|
+
while (Date.now() < deadline) {
|
|
131
|
+
try {
|
|
132
|
+
const res = await fetch(`${base}/api/diagnostics?roomId=${encodeURIComponent(roomId)}`, { signal: AbortSignal.timeout(8_000) });
|
|
133
|
+
if (res.ok) {
|
|
134
|
+
const d = (await res.json());
|
|
135
|
+
lastConnector = Boolean(d.connectorInRoom);
|
|
136
|
+
lastCodes = d.pairingCodesActive ?? 0;
|
|
137
|
+
if (lastConnector && lastCodes > 0) {
|
|
138
|
+
return { ok: true, connectorInRoom: true, pairingCodesActive: lastCodes };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
/* retry */
|
|
144
|
+
}
|
|
145
|
+
await sleep(800);
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
ok: false,
|
|
149
|
+
connectorInRoom: lastConnector,
|
|
150
|
+
pairingCodesActive: lastCodes,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
118
153
|
export function isCursorRunning() {
|
|
119
154
|
try {
|
|
120
155
|
execSync('pgrep -x Cursor', { stdio: 'ignore' });
|
package/dist/pairing-identity.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomBytes, randomUUID } from 'crypto';
|
|
2
2
|
import { generatePairingCode } from './pairing-code.js';
|
|
3
|
+
import { PAIRING_CODE_TTL_MS } from './pairing-ttl.js';
|
|
3
4
|
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
4
5
|
import { homedir, hostname } from 'os';
|
|
5
6
|
import { join } from 'path';
|
|
@@ -33,7 +34,7 @@ export function ensurePairingIdentity(machineLabel) {
|
|
|
33
34
|
clientToken: randomBytes(32).toString('hex'),
|
|
34
35
|
machineLabel: machineLabel?.trim() || defaultMachineLabel(),
|
|
35
36
|
pairingCode: generatePairingCode(),
|
|
36
|
-
pairingCodeExpiresAt: Date.now() +
|
|
37
|
+
pairingCodeExpiresAt: Date.now() + PAIRING_CODE_TTL_MS,
|
|
37
38
|
createdAt: now,
|
|
38
39
|
updatedAt: now,
|
|
39
40
|
};
|
|
@@ -45,7 +46,7 @@ export function refreshPairingCode(identity, machineLabel) {
|
|
|
45
46
|
...identity,
|
|
46
47
|
machineLabel: machineLabel?.trim() || identity.machineLabel,
|
|
47
48
|
pairingCode: generatePairingCode(),
|
|
48
|
-
pairingCodeExpiresAt: Date.now() +
|
|
49
|
+
pairingCodeExpiresAt: Date.now() + PAIRING_CODE_TTL_MS,
|
|
49
50
|
updatedAt: new Date().toISOString(),
|
|
50
51
|
};
|
|
51
52
|
savePairingIdentity(next);
|
package/dist/print-pairing.js
CHANGED
|
@@ -1,35 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { renderBigCode } from './big-code.js';
|
|
2
|
+
import { PAIRING_CODE_TTL_MINUTES } from './pairing-ttl.js';
|
|
3
|
+
/** Pairing code: plain ASCII (A–Z, 0–9) for app input. */
|
|
4
|
+
function pairingCodeAscii(code) {
|
|
5
|
+
return code.replace(/[^A-Z0-9]/g, '').slice(0, 6);
|
|
6
|
+
}
|
|
7
|
+
export function printPairingToTerminal(identity) {
|
|
8
|
+
const code = pairingCodeAscii(identity.pairingCode);
|
|
9
|
+
console.log('Cursor Connect запущен в фоне');
|
|
10
|
+
console.log('');
|
|
11
|
+
console.log('────────────────────────────────────────');
|
|
12
|
+
console.log('');
|
|
7
13
|
console.log(` Компьютер: ${identity.machineLabel}`);
|
|
8
14
|
console.log('');
|
|
9
|
-
console.log(' Введите код в приложении
|
|
15
|
+
console.log(' Введите код в приложении Cursor Connect:');
|
|
10
16
|
console.log('');
|
|
11
|
-
console.log(
|
|
17
|
+
console.log(renderBigCode(code));
|
|
12
18
|
console.log('');
|
|
13
|
-
console.log(' Код одноразовый
|
|
14
|
-
console.log(` Действует
|
|
15
|
-
if (!ready) {
|
|
16
|
-
const parts = [];
|
|
17
|
-
if (!status.bridge)
|
|
18
|
-
parts.push('bridge');
|
|
19
|
-
if (!status.cdp)
|
|
20
|
-
parts.push('Cursor');
|
|
21
|
-
if (!status.connector)
|
|
22
|
-
parts.push('сервер');
|
|
23
|
-
console.log('');
|
|
24
|
-
console.log(` Ожидание: ${parts.join(', ')}…`);
|
|
25
|
-
console.log(' Подождите 10–20 с и снова: cursorconnect status');
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
console.log('');
|
|
29
|
-
console.log(' Готово — введите код на телефоне.');
|
|
30
|
-
}
|
|
19
|
+
console.log(' Код одноразовый');
|
|
20
|
+
console.log(` Действует ${PAIRING_CODE_TTL_MINUTES} мин`);
|
|
31
21
|
console.log('');
|
|
32
22
|
console.log('────────────────────────────────────────');
|
|
33
|
-
console.log('
|
|
34
|
-
console.log(' Консоль можно
|
|
23
|
+
console.log(' Для остановки Cursor Connect введите cursorconnect stop');
|
|
24
|
+
console.log(' Консоль можно закрыть.');
|
|
25
|
+
console.log('');
|
|
35
26
|
}
|
package/dist/repo-root.js
CHANGED
|
@@ -20,7 +20,7 @@ export function loadInstallConfig() {
|
|
|
20
20
|
export function saveInstallConfig(repoRoot) {
|
|
21
21
|
const abs = resolve(repoRoot);
|
|
22
22
|
if (!isValidRepoRoot(abs)) {
|
|
23
|
-
throw new Error(`Не найден bridge в ${abs} (ожидается …/bridge/package.json)`);
|
|
23
|
+
throw new Error(`Не найден Cursor Connect (bridge) в ${abs} (ожидается …/bridge/package.json)`);
|
|
24
24
|
}
|
|
25
25
|
mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
26
26
|
writeFileSync(CONFIG_FILE, JSON.stringify({ repoRoot: abs }, null, 2), { mode: 0o600 });
|
|
@@ -59,7 +59,7 @@ export function resolveRepoRoot() {
|
|
|
59
59
|
const fromDev = devRepoRoot();
|
|
60
60
|
if (fromDev)
|
|
61
61
|
return fromDev;
|
|
62
|
-
throw new Error('Не найден bridge.\n' +
|
|
62
|
+
throw new Error('Не найден Cursor Connect (bridge).\n' +
|
|
63
63
|
' npm install -g cursorconnect && cursorconnect start');
|
|
64
64
|
}
|
|
65
65
|
export function configFilePath() {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursorconnect",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "CLI:
|
|
3
|
+
"version": "0.1.6",
|
|
4
|
+
"description": "CLI: Cursor Connect on Mac + relay pairing — install once, run from anywhere",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
package/version-policy.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"minCliVersion": "0.1.4",
|
|
3
3
|
"minAppVersion": "0.2.2",
|
|
4
|
-
"latestCliVersion": "0.1.
|
|
4
|
+
"latestCliVersion": "0.1.6",
|
|
5
5
|
"latestAppVersion": "0.2.2",
|
|
6
6
|
"updateCliCommand": "npm install -g cursorconnect@latest",
|
|
7
7
|
"updateAppHint": "Обновите CursorConnect в App Store / TestFlight до последней сборки."
|