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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # cursorconnect
2
2
 
3
- CLI для Mac: bridge в фоне, pairing (QR + код), Cursor с CDP.
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) + bridge + код
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,2 @@
1
+ /** Версия для relay auth (должна быть ≥ MIN_CLI_VERSION на VPS). */
2
+ export declare function connectorClientVersion(): string;
@@ -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
- console.warn('[relay-upstream] connect_error:', err.message);
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 bridge (add to bridge/.env)',
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 digits for terminal pairing code. */
2
- const DIGITS = {
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 digits = code.replace(/\D/g, '').slice(0, 6);
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 digits) {
19
- const art = DIGITS[ch] ?? DIGITS['8'];
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
  }
@@ -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
- ? 'Bridge не в этой комнате на relay'
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: 'bridge.local',
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: 'bridge.local',
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 или bridge не отвечает — cursorconnect start',
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, waitRelayConnector, } from './launch.js';
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 порт недоступен — bridge запустится, список чатов будет из архива JSONL.');
59
+ console.warn('[cursorconnect] CDP порт недоступен — Cursor Connect запустится, список чатов будет из архива JSONL.');
58
60
  }
59
61
  if (isBridgeRunning()) {
60
- console.log('Перезапуск bridge…');
62
+ console.error('[cursorconnect] Перезапуск Cursor Connect…');
61
63
  stopBridge();
62
64
  }
63
65
  startBridge(env);
64
- console.log(`Bridge в фоне ${BRIDGE_LOG_FILE}`);
65
- // 2) Ждём, пока bridge реально подключится к workbench (health.cdp), не только :9222.
66
- const health = await waitBridgeHealth(45_000, { requireCdp: cdpPortOk });
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
- printPairingToTerminal(refreshed, {
75
- bridge: health.ok,
76
- cdp: cdpReady,
77
- connector: connectorOk,
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('Bridge остановлен');
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.log('Identity не найден — запустите: cursorconnect start');
112
- return;
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) + bridge + код
149
+ start — CDP (авто-перезапуск Cursor) + Cursor Connect + код
169
150
  start -r — то же (явный перезапуск Cursor)
170
151
  start --no-restart-cursor — не трогать Cursor (спросит y/n в TTY)
171
- stop — остановить bridge
152
+ stop — остановить Cursor Connect
172
153
  status — код и статус
173
- diagnose — проверка relay / bridge / socket (без гаданий)
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' });
@@ -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() + 10 * 60_000,
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() + 10 * 60_000,
49
+ pairingCodeExpiresAt: Date.now() + PAIRING_CODE_TTL_MS,
49
50
  updatedAt: new Date().toISOString(),
50
51
  };
51
52
  savePairingIdentity(next);
@@ -0,0 +1,3 @@
1
+ /** Срок жизни pairing-кода (Mac ↔ relay). */
2
+ export const PAIRING_CODE_TTL_MS = 10 * 60_000;
3
+ export const PAIRING_CODE_TTL_MINUTES = 10;
@@ -1,35 +1,26 @@
1
- export function printPairingToTerminal(identity, status) {
2
- const expiresIn = Math.max(0, Math.floor((identity.pairingCodeExpiresAt - Date.now()) / 1000));
3
- const ready = status.bridge && status.cdp && status.connector;
4
- const code = identity.pairingCode;
5
- console.log('\n────────────────────────────────────────');
6
- console.log(' CursorConnect');
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(' Введите код в приложении CursorConnect:');
15
+ console.log(' Введите код в приложении Cursor Connect:');
10
16
  console.log('');
11
- console.log(` ${code}`);
17
+ console.log(renderBigCode(code));
12
18
  console.log('');
13
- console.log(' Код одноразовый — после подключения снова: cursorconnect start');
14
- console.log(` Действует ~${Math.max(1, Math.floor(expiresIn / 60))} мин`);
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(' Bridge в фоне · cursorconnect stop');
34
- console.log(' Консоль можно закрыть.\n');
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",
4
- "description": "CLI: CursorConnect bridge + relay pairing — install once, run from anywhere",
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": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "minCliVersion": "0.1.4",
3
3
  "minAppVersion": "0.2.2",
4
- "latestCliVersion": "0.1.4",
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 до последней сборки."