cursorconnect 0.1.4 → 0.1.5
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/dist/big-code.js +36 -5
- package/dist/bridge-dir.js +1 -1
- package/dist/diagnose.js +4 -4
- package/dist/index.js +18 -55
- package/dist/launch.js +7 -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/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
|
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
|
}
|
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
|
@@ -54,32 +54,26 @@ async function cmdStart(argv) {
|
|
|
54
54
|
noRestartCursor,
|
|
55
55
|
});
|
|
56
56
|
if (!cdpPortOk) {
|
|
57
|
-
console.warn('[cursorconnect] CDP порт недоступен —
|
|
57
|
+
console.warn('[cursorconnect] CDP порт недоступен — Cursor Connect запустится, список чатов будет из архива JSONL.');
|
|
58
58
|
}
|
|
59
59
|
if (isBridgeRunning()) {
|
|
60
|
-
console.
|
|
60
|
+
console.error('[cursorconnect] Перезапуск Cursor Connect…');
|
|
61
61
|
stopBridge();
|
|
62
62
|
}
|
|
63
63
|
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);
|
|
64
|
+
let health = await waitBridgeHealth(12_000, { requireCdp: false });
|
|
65
|
+
if (health.ok && cdpPortOk && !health.cdp) {
|
|
66
|
+
health = await waitBridgeHealth(15_000, { requireCdp: true });
|
|
73
67
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
cdp:
|
|
77
|
-
|
|
78
|
-
|
|
68
|
+
await waitRelayConnector(relayUrl, 12_000);
|
|
69
|
+
if (cdpPortOk && health.ok && !health.cdp) {
|
|
70
|
+
console.error(`[cursorconnect] CDP не подключён (cdp:false) · лог: ${BRIDGE_LOG_FILE}`);
|
|
71
|
+
}
|
|
72
|
+
printPairingToTerminal(refreshed);
|
|
79
73
|
}
|
|
80
74
|
async function cmdStop() {
|
|
81
75
|
stopBridge();
|
|
82
|
-
console.log('
|
|
76
|
+
console.log('Cursor Connect остановлен');
|
|
83
77
|
}
|
|
84
78
|
async function cmdInit(pathArg) {
|
|
85
79
|
const target = pathArg?.trim() || process.cwd();
|
|
@@ -104,43 +98,12 @@ async function cmdDiagnose() {
|
|
|
104
98
|
async function cmdStatus() {
|
|
105
99
|
const identity = loadPairingIdentity();
|
|
106
100
|
ensureUserConfig();
|
|
107
|
-
const bridgeDotEnv = join(resolveBridgeDir(), '.env');
|
|
108
|
-
const { relayUrl } = resolveRelayConfig([bridgeDotEnv]);
|
|
109
|
-
console.log('Bridge running:', isBridgeRunning());
|
|
110
101
|
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
|
-
});
|
|
102
|
+
console.error('Identity не найден — запустите: cursorconnect start');
|
|
103
|
+
process.exit(1);
|
|
143
104
|
}
|
|
105
|
+
console.error(`[cursorconnect] Cursor Connect работает: ${isBridgeRunning()}`);
|
|
106
|
+
printPairingToTerminal(identity);
|
|
144
107
|
}
|
|
145
108
|
async function main() {
|
|
146
109
|
const argv = process.argv.slice(2);
|
|
@@ -165,12 +128,12 @@ async function main() {
|
|
|
165
128
|
default:
|
|
166
129
|
console.log(`Usage: cursorconnect <command>
|
|
167
130
|
|
|
168
|
-
start — CDP (авто-перезапуск Cursor) +
|
|
131
|
+
start — CDP (авто-перезапуск Cursor) + Cursor Connect + код
|
|
169
132
|
start -r — то же (явный перезапуск Cursor)
|
|
170
133
|
start --no-restart-cursor — не трогать Cursor (спросит y/n в TTY)
|
|
171
|
-
stop — остановить
|
|
134
|
+
stop — остановить Cursor Connect
|
|
172
135
|
status — код и статус
|
|
173
|
-
diagnose — проверка relay /
|
|
136
|
+
diagnose — проверка relay / Cursor Connect / socket (без гаданий)
|
|
174
137
|
init [path] — только для разработки (клон репо)
|
|
175
138
|
|
|
176
139
|
Установка (из любой папки):
|
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
|
}
|
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.5",
|
|
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": {
|